-- | -- Module: Test.QuickCheck.Classes.Euclidean -- Copyright: (c) 2019 Andrew Lelechenko -- Licence: BSD3 -- {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -Wall #-} #if !HAVE_SEMIRINGS module Test.QuickCheck.Classes.Euclidean where #else module Test.QuickCheck.Classes.Euclidean ( gcdDomainLaws , euclideanLaws ) where import Prelude hiding (quotRem, quot, rem, gcd, lcm) import Data.Maybe import Data.Proxy (Proxy) import Data.Euclidean import Data.Semiring (Semiring(..)) import Test.QuickCheck hiding ((.&.)) import Test.QuickCheck.Property (Property) import Test.QuickCheck.Classes.Internal (Laws(..)) -- | Test that a 'GcdDomain' instance obey several laws. -- -- Check that 'divide' is an inverse of times: -- -- * @y \/= 0 => (x * y) \`divide\` y == Just x@, -- * @y \/= 0, x \`divide\` y == Just z => x == z * y@. -- -- Check that 'gcd' is a common divisor and is a multiple of any common divisor: -- -- * @x \/= 0, y \/= 0 => isJust (x \`divide\` gcd x y) && isJust (y \`divide\` gcd x y)@, -- * @z \/= 0 => isJust (gcd (x * z) (y * z) \`divide\` z)@. -- -- Check that 'lcm' is a common multiple and is a factor of any common multiple: -- -- * @x \/= 0, y \/= 0 => isJust (lcm x y \`divide\` x) && isJust (lcm x y \`divide\` y)@, -- * @x \/= 0, y \/= 0, isJust (z \`divide\` x), isJust (z \`divide\` y) => isJust (z \`divide\` lcm x y)@. -- -- Check that 'gcd' of 'coprime' numbers is a unit of the semiring (has an inverse): -- -- * @y \/= 0, coprime x y => isJust (1 \`divide\` gcd x y)@. gcdDomainLaws :: (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Laws gcdDomainLaws p = Laws "GcdDomain" [ ("divide1", divideLaw1 p) , ("divide2", divideLaw2 p) , ("gcd1", gcdLaw1 p) , ("gcd2", gcdLaw2 p) , ("lcm1", lcmLaw1 p) , ("lcm2", lcmLaw2 p) , ("coprime", coprimeLaw p) ] divideLaw1 :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property divideLaw1 _ = property $ \(x :: a) y -> y /= zero ==> (x `times` y) `divide` y === Just x divideLaw2 :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property divideLaw2 _ = property $ \(x :: a) y -> y /= zero ==> maybe (property True) (\z -> x === z `times` y) (x `divide` y) gcdLaw1 :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property gcdLaw1 _ = property $ \(x :: a) y -> x /= zero || y /= zero ==> isJust (x `divide` gcd x y) .&&. isJust (y `divide` gcd x y) gcdLaw2 :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property gcdLaw2 _ = property $ \(x :: a) y z -> z /= zero ==> isJust (gcd (x `times` z) (y `times` z) `divide` z) lcmLaw1 :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property lcmLaw1 _ = property $ \(x :: a) y -> x /= zero && y /= zero ==> isJust (lcm x y `divide` x) .&&. isJust (lcm x y `divide` y) lcmLaw2 :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property lcmLaw2 _ = property $ \(x :: a) y z -> x /= zero && y /= zero ==> isNothing (z `divide` x) .||. isNothing (z `divide` y) .||. isJust (z `divide` lcm x y) coprimeLaw :: forall a. (Eq a, GcdDomain a, Arbitrary a, Show a) => Proxy a -> Property coprimeLaw _ = property $ \(x :: a) y -> y /= zero ==> coprime x y === isJust (one `divide` gcd x y) -- | Test that a 'Euclidean' instance obey laws of a Euclidean domain. -- -- * @y \/= 0, r == x \`rem\` y => r == 0 || degree r < degree y@, -- * @y \/= 0, (q, r) == x \`quotRem\` y => x == q * y + r@, -- * @y \/= 0 => x \`quot\` x y == fst (x \`quotRem\` y)@, -- * @y \/= 0 => x \`rem\` x y == snd (x \`quotRem\` y)@. euclideanLaws :: (Eq a, Euclidean a, Arbitrary a, Show a) => Proxy a -> Laws euclideanLaws p = Laws "Euclidean" [ ("degree", degreeLaw p) , ("quotRem", quotRemLaw p) , ("quot", quotLaw p) , ("rem", remLaw p) ] degreeLaw :: forall a. (Eq a, Euclidean a, Arbitrary a, Show a) => Proxy a -> Property degreeLaw _ = property $ \(x :: a) y -> y /= zero ==> let (_, r) = x `quotRem` y in (r === zero .||. degree r < degree y) quotRemLaw :: forall a. (Eq a, Euclidean a, Arbitrary a, Show a) => Proxy a -> Property quotRemLaw _ = property $ \(x :: a) y -> y /= zero ==> let (q, r) = x `quotRem` y in x === (q `times` y) `plus` r quotLaw :: forall a. (Eq a, Euclidean a, Arbitrary a, Show a) => Proxy a -> Property quotLaw _ = property $ \(x :: a) y -> y /= zero ==> quot x y === fst (quotRem x y) remLaw :: forall a. (Eq a, Euclidean a, Arbitrary a, Show a) => Proxy a -> Property remLaw _ = property $ \(x :: a) y -> y /= zero ==> rem x y === snd (quotRem x y) #endif