{-# LANGUAGE NoImplicitPrelude #-} module Algebra.Units ( {- * Class -} C, isUnit, stdAssociate, stdUnit, stdUnitInv, {- * Standard implementations for instances -} intQuery, intAssociate, intStandard, intStandardInverse, {- * Properties -} propComposition, propInverseUnit, propUniqueAssociate, propAssociateProduct, ) where import qualified Algebra.IntegralDomain as Integral import qualified Algebra.Ring as Ring import qualified Algebra.Additive as Additive import qualified Algebra.ZeroTestable as ZeroTestable import qualified Algebra.Laws as Laws import Algebra.IntegralDomain (div) import Algebra.Ring (one, (*)) import Algebra.Additive (negate) import Algebra.ZeroTestable (isZero) import PreludeBase import Prelude (Integer, Int) import qualified Prelude as P import Test.QuickCheck ((==>), Property) {- | This class lets us deal with the units in a ring. 'isUnit' tells whether an element is a unit. The other operations let us canonically write an element as a unit times another element. Two elements a, b of a ring R are _associates_ if a=b*u for a unit u. For an element a, we want to write it as a=b*u where b is an associate of a. The map (a->b) is called "StandardAssociate" by Gap, "unitCanonical" by Axiom, and "canAssoc" by DoCon. The map (a->u) is called "canInv" by DoCon and "unitNormal(x).unit" by Axiom. The laws are > stdAssociate x * stdUnit x === x > stdUnit x * stdUnitInv x === 1 > isUnit u ==> stdAssociate x === stdAssociate (x*u) Currently some algorithms assume > stdAssociate(x*y) === stdAssociate x * stdAssociate y Minimal definition: 'isUnit' and ('stdUnit' or 'stdUnitInv') and optionally 'stdAssociate' -} class (Integral.C a) => C a where isUnit :: a -> Bool stdAssociate, stdUnit, stdUnitInv :: a -> a stdAssociate x = x * stdUnitInv x stdUnit x = div one (stdUnitInv x) -- should be safeDiv stdUnitInv x = div one (stdUnit x) {- * Instances for atomic types -} intQuery :: (P.Integral a, Ring.C a) => a -> Bool intQuery = flip elem [one, negate one] {- constraint must be replaced by NumericPrelude.Real -} intAssociate, intStandard, intStandardInverse :: (P.Integral a, Ring.C a, ZeroTestable.C a) => a -> a intAssociate = P.abs intStandard x = if isZero x then one else P.signum x intStandardInverse = intStandard instance C Int where isUnit = intQuery stdAssociate = intAssociate stdUnit = intStandard stdUnitInv = intStandardInverse instance C Integer where isUnit = intQuery stdAssociate = intAssociate stdUnit = intStandard stdUnitInv = intStandardInverse {- fieldQuery = not . isZero fieldAssociate = 1 fieldStandard x = if isZero x then 1 else x fieldStandardInverse x = if isZero x then 1 else recip x -} propComposition :: (Eq a, C a) => a -> Bool propInverseUnit :: (Eq a, C a) => a -> Bool propUniqueAssociate :: (Eq a, C a) => a -> a -> Property propAssociateProduct :: (Eq a, C a) => a -> a -> Bool propComposition x = stdAssociate x * stdUnit x == x propInverseUnit x = stdUnit x * stdUnitInv x == one propUniqueAssociate u x = isUnit u ==> stdAssociate x == stdAssociate (x*u) {- | Currently some algorithms assume this property. -} propAssociateProduct = Laws.homomorphism stdAssociate (*) (*)