packages feed

tower-0.1.0: test/test.hs

{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE DataKinds #-}

module Main where

import Protolude hiding ((+),(-),(*),(/),zero,one,negate,recip,div,mod,rem,quot, Integral(..))
import Test.Tasty (TestName, TestTree, testGroup, defaultMain)
import Test.Tasty.QuickCheck
import Tower.Algebra
import Tower.VectorU
import Tower.VectorA

data LawArity a =
    Unary (a -> Bool) |
    Binary (a -> a -> Bool) |
    Ternary (a -> a -> a -> Bool) |
    Ornary (a -> a -> a -> a -> Bool) |
    Uniary (a -> Property)

type Law a = (TestName, LawArity a)

testLawOf  :: (Arbitrary a, Show a) => [a] -> Law a -> TestTree
testLawOf _ (name, Unary f) = testProperty name f
testLawOf _ (name, Binary f) = testProperty name f
testLawOf _ (name, Ternary f) = testProperty name f
testLawOf _ (name, Ornary f) = testProperty name f
testLawOf _ (name, Uniary f) = testProperty name f

tests :: TestTree
tests = testGroup "everything"
    [ testGroup "Int - Additive" $ testLawOf ([]::[Int]) <$> additiveLaws
    , testGroup "Int - Additive Group" $ testLawOf ([]::[Int]) <$> additiveGroupLaws
    , testGroup "Int - Multiplicative" $ testLawOf ([]::[Int]) <$> multiplicativeLaws
    , testGroup "Int - Distributive" $ testLawOf ([]::[Int]) <$> distributiveLaws
    , testGroup "Int - Integral" $ testLawOf ([]::[Int]) <$> integralLaws
    , testGroup "Float - Additive" $ testLawOf ([]::[Float]) <$> additiveFloatLaws
    , testGroup "Float - Additive Group" $ testLawOf ([]::[Float]) <$> additiveGroupLaws
    , testGroup "Float - Multiplicative" $ testLawOf ([]::[Float]) <$> multiplicativeFloatLaws
    , testGroup "Float - MultiplicativeGroup" $ testLawOf ([]::[Float]) <$> fieldFloatLaws
    , testGroup "Float - Distributive" $ testLawOf ([]::[Float]) <$> distributiveFloatLaws
    , testGroup "UVector 5 Int - Additive" $ testLawOf ([]::[VectorU 5 Int]) <$> additiveLaws
    , testGroup "VectorU 5 Int - Additive Group" $ testLawOf ([]::[VectorU 5 Int]) <$> additiveGroupLaws
    , testGroup "VectorU 5 Int - Multiplicative" $ testLawOf ([]::[VectorU 5 Int]) <$> multiplicativeLaws
    , testGroup "VectorU 5 Int - Distributive" $ testLawOf ([]::[VectorU 5 Int]) <$> distributiveLaws
    -- , testGroup "VectorU 5 Int - Integral" $ testLawOf ([]::[VectorU 5 Int]) <$> integralLaws
    , testGroup "VectorU 5 Float - Additive" $ testLawOf ([]::[VectorU 5 Float]) <$> additiveFloatLaws
    , testGroup "VectorU 5 Float - Additive Group" $ testLawOf ([]::[VectorU 5 Float]) <$> additiveGroupLaws
    , testGroup "VectorU 5 Float - Multiplicative" $ testLawOf ([]::[VectorU 5 Float]) <$> multiplicativeFloatLaws
    , testGroup "VectorU 5 Float - MultiplicativeGroup" $ testLawOf ([]::[VectorU 5 Float]) <$> fieldFloatLaws
    , testGroup "VectorU 5 Float - Distributive" $ testLawOf ([]::[VectorU 5 Float]) <$> distributiveFloatLaws
    , testGroup "VectorA Int - Additive" $ testLawOf ([]::[VectorA 5 [] Int]) <$> additiveLaws
    , testGroup "VectorA Int - Additive Group" $ testLawOf ([]::[VectorA 5 [] Int]) <$> additiveGroupLaws
    , testGroup "VectorA Int - Multiplicative" $ testLawOf ([]::[VectorA 5 [] Int]) <$> multiplicativeLaws
    , testGroup "VectorA Int - Distributive" $ testLawOf ([]::[VectorA 5 [] Int]) <$> distributiveLaws
    ]

main :: IO ()
main = defaultMain tests

additiveLaws ::
    ( Eq a
    , Additive a
    ) => [Law a]
additiveLaws =
    [ ("associative: a + b = b + a", Ternary (\a b c -> (a + b) + c == a + (b + c)))
    , ("left id: zero + a = a", Unary (\a -> zero + a == a))
    , ("right id: a + zero = a", Unary (\a -> a + zero == a))
    , ("commutative: a + b == b + a", Binary (\a b -> a + b == b + a))
    ]

additiveFloatLaws ::
    ( Eq a
    , Additive a
    , Show a
    , Arbitrary a
    ) => [Law a]
additiveFloatLaws =
    [ ("associative: a + b = b + a", Uniary $ expectFailure . (\a b c -> (a + b) + c == a + (b + c)))
    , ("left id: zero + a = a", Unary (\a -> zero + a == a))
    , ("right id: a + zero = a", Unary (\a -> a + zero == a))
    , ("commutative: a + b == b + a", Binary (\a b -> a + b == b + a))
    ]


additiveGroupLaws ::
    ( Eq a
    , AdditiveGroup a
    ) => [Law a]
additiveGroupLaws =
    [ ("minus: a - a = zero", Unary (\a -> (a - a) == zero))
    , ("negate minus: negate a == zero - a", Unary (\a -> negate a == zero - a))
    , ("negate cancel: negate a + a == zero", Unary (\a -> negate a + a == zero))
    ]

multiplicativeLaws ::
    ( Eq a
    , Multiplicative a
    ) => [Law a]
multiplicativeLaws =
    [ ("associative: a * b = b * a", Ternary (\a b c -> (a * b) * c == a * (b * c)))
    , ("left id: one * a = a", Unary (\a -> one * a == a))
    , ("right id: a * one = a", Unary (\a -> a * one == a))
    , ("commutative: a * b == b * a", Binary (\a b -> a * b == b * a))
    ]


aboutEqual :: (AdditiveGroup a, Ord a) => a -> a -> a -> Bool
aboutEqual eps a b = a - b <= eps && b - a <= eps

multiplicativeFloatLaws ::
    ( Eq a
    , Show a
    , Arbitrary a
    , Multiplicative a
    ) => [Law a]
multiplicativeFloatLaws =
    [ ("associative: a * b = b * a", Uniary $ expectFailure .  (\a b c -> (a * b) * c == a * (b * c)))
    , ("left id: one * a = a", Unary (\a -> one * a == a))
    , ("right id: a * one = a", Unary (\a -> a * one == a))
    , ("commutative: a * b == b * a", Binary (\a b -> a * b == b * a))
    ]

fieldLaws ::
    ( Eq a
    , MultiplicativeGroup a
    , AdditiveGroup a
    ) => [Law a]
fieldLaws =
    [ ("divide: a == zero || a / a = one", Unary (\a -> a == zero || (a / a) == one))
    , ("recip divide: recip a == one / a", Unary (\a -> recip a == one / a))
    , ("recip left: recip a * a == one", Unary (\a -> a == zero || recip a * a == one))
    , ("recip right: a * recip a == one", Unary (\a -> a == zero || a * recip a == one))
    ]

fieldFloatLaws ::
    ( MultiplicativeGroup a
    , AdditiveGroup a
    , Eq a
    ) => [Law a]
fieldFloatLaws =
    [ ("divide: a == zero || a / a == one", Uniary $ expectFailure . (\a -> a == zero || a / a == one))
    , ("recip divide: recip a == one / a", Unary (\a -> recip a == one / a))
    , ("recip left: recip a * a == one", Uniary $ expectFailure . (\a -> a == zero || recip a * a == one))
    , ("recip right: a * recip a == one", Uniary $ expectFailure . (\a -> a == zero || a * recip a == one))
    ]

distributiveLaws ::
    ( Eq a
    , Distributive a
    , Multiplicative a
    ) => [Law a]
distributiveLaws =
    [ ("annihilation: a * zero == zero", Unary (\a -> a * zero == zero))
    , ("left distributivity: a * (b + c) == a * b + a * c", Ternary (\a b c -> a * (b + c) == a * b + a * c))
    , ("right distributivity: (a + b) * c == a * c + b * c", Ternary (\a b c -> (a + b) * c == a * c + b * c))
    ]

distributiveFloatLaws ::
    ( Eq a
    , Distributive a
    , Multiplicative a
    , Show a
    , Arbitrary a
    ) => [Law a]
distributiveFloatLaws =
    [ ("annihilation: a * zero == zero", Unary (\a -> a * zero == zero))
    , ("left distributivity: a * (b + c) == a * b + a * c", Uniary $ expectFailure . (\a b c -> a * (b + c) == a * b + a * c))
    , ("right distributivity: (a + b) * c == a * c + b * c", Uniary $ expectFailure . (\a b c -> (a + b) * c == a * c + b * c))
    ]


integralLaws ::
    ( Eq a
    , Integral a
    ) => [Law a]
integralLaws =
    [ ("integral divmod: b == zero || b * (a `div` b) + (a `mod` b) == a", Binary (\a b -> b == zero || b * (a `div` b) + (a `mod` b) == a))
   ]

todoLaws ::
    ( -- Eq a
    -- , AdditiveModule s a
    ) => [Law a]
todoLaws =
    [ -- (a1+a2) +. s == a1 +. a2 +. s
      -- m2 s = s *. (m1 + m2) == s*.m1 + s*.m2
      -- m s1 s2 = (s1+s2)*.m == s1*.m + s2*.m
      -- s1 s2 = s1*.(s2*.m) == (s1*s2)*.m
      -- m = 1 *. m == m
      -- free modules
      -- m1 m2 = m1.*.m2 == m2.*.m1
      -- m1 m2 m3 = m1.*.(m2.*.m3) == (m1.*.m2).*.m3
      -- m = m == m.*.ones
      -- Banach
      -- v1 v2 = size (v1 - v2) == distance v1 v2
      -- v = isZero v || size (normalize v) == 1
      -- Metric
      -- v1 v2 = distance v1 v2 >= 0
      -- v1 v2 = v1 == v2 == distance v1 v2 == 0
      -- v1 v2 = distance v1 v2 == distance v2 v1
      -- m1 m2 m3 = distance m1 m2 <= distance m1 m3 + distance m2 m3
      --         && distance m1 m3 <= distance m1 m2 + distance m2 m3
      --         && distance m2 m3 <= distance m1 m3 + distance m2 m1
    ]