module Number.NonNegativeChunky
(T, fromChunks, toChunks, fromNumber, toNumber, fromChunky98, toChunky98,
minMaxDiff, normalize, isNull, isPositive,
divModLazy, divModStrict, ) where
import qualified Numeric.NonNegative.Chunky as Chunky98
import qualified Numeric.NonNegative.Class as NonNeg98
import qualified Algebra.NonNegative as NonNeg
import qualified Algebra.Absolute as Absolute
import qualified Algebra.Ring as Ring
import qualified Algebra.Additive as Additive
import qualified Algebra.ToInteger as ToInteger
import qualified Algebra.ToRational as ToRational
import qualified Algebra.IntegralDomain as Integral
import qualified Algebra.RealIntegral as RealIntegral
import qualified Algebra.ZeroTestable as ZeroTestable
import qualified Algebra.Monoid as Monoid
import qualified Data.Monoid as Mn98
import qualified Data.Semigroup as Sg98
import Control.Monad (liftM, liftM2, )
import Data.Tuple.HT (mapFst, mapSnd, mapPair, )
import Test.QuickCheck (Arbitrary(arbitrary))
import NumericPrelude.Numeric
import NumericPrelude.Base
import qualified Prelude as P98
newtype T a = Cons {decons :: [a]}
fromChunks :: NonNeg.C a => [a] -> T a
fromChunks = Cons
toChunks :: NonNeg.C a => T a -> [a]
toChunks = decons
fromChunky98 :: (NonNeg.C a, NonNeg98.C a) => Chunky98.T a -> T a
fromChunky98 = fromChunks . Chunky98.toChunks
toChunky98 :: (NonNeg.C a, NonNeg98.C a) => T a -> Chunky98.T a
toChunky98 = Chunky98.fromChunks . toChunks
fromNumber :: NonNeg.C a => a -> T a
fromNumber = fromChunks . (:[])
toNumber :: NonNeg.C a => T a -> a
toNumber = Monoid.cumulate . toChunks
lift2 :: NonNeg.C a => ([a] -> [a] -> [a]) -> (T a -> T a -> T a)
lift2 f x y =
fromChunks $ f (toChunks x) (toChunks y)
normalize :: NonNeg.C a => T a -> T a
normalize = fromChunks . filter (> NonNeg.zero) . toChunks
isNullList :: NonNeg.C a => [a] -> Bool
isNullList = null . filter (> NonNeg.zero)
isNull :: NonNeg.C a => T a -> Bool
isNull = isNullList . toChunks
isPositive :: NonNeg.C a => T a -> Bool
isPositive = not . isNull
isNullListZT :: ZeroTestable.C a => [a] -> Bool
isNullListZT = null . filter (not . isZero)
isNullZT :: ZeroTestable.C a => T a -> Bool
isNullZT = isNullListZT . decons
check :: String -> Bool -> a -> a
check funcName b x =
if b
then x
else error ("Numeric.NonNegative.Chunky."++funcName++": negative number")
glue :: (NonNeg.C a) => [a] -> [a] -> ([a], (Bool, [a]))
glue [] ys = ([], (True, ys))
glue xs [] = ([], (False, xs))
glue (x:xs) (y:ys) =
let (z,~(zs,brs)) =
flip mapSnd (NonNeg.split x y) $
\(b,d) ->
if b
then glue xs $
if NonNeg.zero == d
then ys else d:ys
else glue (d:xs) ys
in (z:zs,brs)
minMaxDiff :: (NonNeg.C a) => T a -> T a -> (T a, (Bool, T a))
minMaxDiff (Cons xs) (Cons ys) =
let (zs, (b, rs)) = glue xs ys
in (Cons zs, (b, Cons rs))
equalList :: (NonNeg.C a) => [a] -> [a] -> Bool
equalList x y =
isNullList $ snd $ snd $ glue x y
compareList :: (NonNeg.C a) => [a] -> [a] -> Ordering
compareList x y =
let (b,r) = snd $ glue x y
in if isNullList r
then EQ
else if b then LT else GT
minList :: (NonNeg.C a) => [a] -> [a] -> [a]
minList x y =
fst $ glue x y
maxList :: (NonNeg.C a) => [a] -> [a] -> [a]
maxList x y =
let (z,~(_,r)) = glue x y in z++r
instance (NonNeg.C a) => Eq (T a) where
(Cons x) == (Cons y) = equalList x y
instance (NonNeg.C a) => Ord (T a) where
compare (Cons x) (Cons y) = compareList x y
min = lift2 minList
max = lift2 maxList
instance (NonNeg.C a) => NonNeg.C (T a) where
split (Cons xs) (Cons ys) =
let (zs, ~(b, rs)) = glue xs ys
in (Cons zs, (b, Cons rs))
instance (ZeroTestable.C a) => ZeroTestable.C (T a) where
isZero = isNullZT
instance (NonNeg.C a) => Additive.C (T a) where
zero = Monoid.idt
(+) = (Monoid.<*>)
(Cons x) (Cons y) =
let (b,d) = snd $ glue x y
d' = Cons d
in check "-" (not b || isNull d') d'
negate x = check "negate" (isNull x) x
instance (Ring.C a, NonNeg.C a) => Ring.C (T a) where
one = fromNumber one
(*) = lift2 (liftM2 (*))
fromInteger = fromNumber . fromInteger
instance (Ring.C a, ZeroTestable.C a, NonNeg.C a) => Absolute.C (T a) where
abs = id
signum = fromNumber . (\b -> if b then one else zero) . isPositive
instance (ToInteger.C a, NonNeg.C a) => ToInteger.C (T a) where
toInteger = sum . map toInteger . toChunks
instance (ToRational.C a, NonNeg.C a) => ToRational.C (T a) where
toRational = sum . map toRational . toChunks
instance (RealIntegral.C a, NonNeg.C a) => RealIntegral.C (T a) where
quot = div
rem = mod
quotRem = divMod
instance (Ord a, Integral.C a, NonNeg.C a) => Integral.C (T a) where
divMod x y =
mapSnd fromNumber $
divModStrict x (toNumber y)
divModLazy ::
(Ring.C a, NonNeg.C a) =>
T a -> T a -> (T a, T a)
divModLazy x0 y0 =
let y = toChunks y0
recourse x =
let (r,~(b,d)) = glue y x
in if not b
then ([], r)
else mapFst (one:) (recourse d)
in mapPair
(fromChunks, fromChunks)
(recourse (toChunks x0))
divModStrict ::
(Integral.C a, NonNeg.C a) =>
T a -> a -> (T a, a)
divModStrict x0 y =
let recourse [] r = ([], r)
recourse (x:xs) r0 =
case divMod (x+r0) y of
(q,r1) -> mapFst (q:) $ recourse xs r1
in mapFst fromChunks $ recourse (toChunks x0) zero
instance (Show a) => Show (T a) where
showsPrec p x =
showParen (p>10)
(showString "Chunky.fromChunks " . showsPrec 10 (decons x))
instance (NonNeg.C a, Arbitrary a) => Arbitrary (T a) where
arbitrary = liftM Cons arbitrary
fromChunky98_ :: (NonNeg98.C a) => Chunky98.T a -> T a
fromChunky98_ = Cons . Chunky98.toChunks
toChunky98_ :: (NonNeg98.C a) => T a -> Chunky98.T a
toChunky98_ = Chunky98.fromChunks . decons
fromNumber_ :: a -> T a
fromNumber_ = Cons . (:[])
lift98_1 ::
(NonNeg98.C a, NonNeg98.C b) =>
(Chunky98.T a -> Chunky98.T b) -> T a -> T b
lift98_1 f a = fromChunky98_ (f (toChunky98_ a))
lift98_2 ::
(NonNeg98.C a, NonNeg98.C b, NonNeg98.C c) =>
(Chunky98.T a -> Chunky98.T b -> Chunky98.T c) -> T a -> T b -> T c
lift98_2 f a b = fromChunky98_ (f (toChunky98_ a) (toChunky98_ b))
notImplemented :: String -> a
notImplemented name =
error $ "Number.NonNegativeChunky: method " ++ name ++ " cannot be implemented"
instance (NonNeg98.C a, P98.Num a) => P98.Num (T a) where
fromInteger = fromNumber_ . P98.fromInteger
negate = lift98_1 P98.negate
(+) = lift98_2 (P98.+)
(*) = lift98_2 (P98.*)
abs = lift98_1 P98.abs
signum = lift98_1 P98.signum
instance (NonNeg98.C a, P98.Fractional a) => P98.Fractional (T a) where
fromRational = fromNumber_ . P98.fromRational
(/) = notImplemented "(/)"
instance (NonNeg.C a) => Sg98.Semigroup (T a) where
(<>) = (Monoid.<*>)
instance (NonNeg.C a) => Mn98.Monoid (T a) where
mempty = Monoid.idt
mappend = (Monoid.<*>)
instance (NonNeg.C a) => Monoid.C (T a) where
idt = Cons []
(<*>) = lift2 (++)