padic-0.1.0.0: src/Math/NumberTheory/Padic/Rational.hs
{-# OPTIONS_HADDOCK hide, prune, ignore-exports #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Math.NumberTheory.Padic.Rational where
import Data.List (intercalate)
import Data.Ratio
import Data.Mod
import Data.Word
import GHC.TypeLits (Nat, natVal)
import Math.NumberTheory.Padic.Types
import Math.NumberTheory.Padic.Analysis
import Math.NumberTheory.Padic.Integer
------------------------------------------------------------
type instance Padic Rational p = Q' p (SufficientPrecision Word p)
type instance Padic (Ratio Int) p = Q' p (SufficientPrecision Int p)
type instance Padic (Ratio Word8) p = Q' p (SufficientPrecision Word8 p)
type instance Padic (Ratio Word16) p = Q' p (SufficientPrecision Word16 p)
type instance Padic (Ratio Word32) p = Q' p (SufficientPrecision Word32 p)
type instance Padic (Ratio Word64) p = Q' p (SufficientPrecision Word64 p)
type instance Padic (Ratio Word) p = Q' p (SufficientPrecision Word64 p)
------------------------------------------------------------
-- | Rational p-adic number (an element of \(\mathbb{Q}_p\)) with default precision.
type Q p = Q' p (SufficientPrecision Word32 p)
-- | Rational p-adic number with explicitly specified precision.
data Q' (p :: Nat) (prec :: Nat) = Q' !(Z' p prec) !Int
instance Radix p prec => PadicNum (Q' p prec) where
type Unit (Q' p prec) = Z' p prec
type Digit (Q' p prec) = Digit (Z' p prec)
{-# INLINE precision #-}
precision = fromIntegral . natVal
{-# INLINE radix #-}
radix (Q' u _) = radix u
{-# INLINE digits #-}
digits (Q' u v) = replicate v 0 ++ toRadix (lifted u)
{-# INLINE fromDigits #-}
fromDigits ds = normalize $ Q' (fromDigits ds) 0
{-# INLINE lifted #-}
lifted (Q' u _) = lifted u
{-# INLINE mkUnit #-}
mkUnit ds = normalize $ Q' (mkUnit ds) 0
{-# INLINE fromUnit #-}
fromUnit (u, v) = Q' u v
splitUnit n@(Q' u v) =
let pr = precision n
(u', v') = splitUnit u
in if v + v' > pr then (0, pr) else (u', v + v')
isInvertible = isInvertible . unit . normalize
inverse n = do r <- inverse (unit n)
return $ fromUnit (r, - valuation n)
instance Radix p prec => Show (Q' p prec) where
show n = si ++ sep ++ "." ++ sep ++ sf
where
(u, k) = splitUnit (normalize n)
pr = precision n
ds = digits u
(f, i) =
case compare k 0 of
EQ -> ([0], ds)
GT -> ([0], replicate k 0 ++ ds)
LT -> splitAt (-k) (ds ++ replicate (pr + k) 0)
sf = intercalate sep $ showD <$> reverse f
si =
case findCycle pr i of
Nothing
| null i -> "0"
| length i > pr -> ell ++ toString (take pr i)
| otherwise -> toString i
Just ([], [0]) -> "0"
Just (pref, [0]) -> toString pref
Just (pref, cyc)
| length pref + length cyc <= pr ->
let sp = toString pref
sc = toString cyc
in "(" ++ sc ++ ")" ++ sep ++ sp
| otherwise -> ell ++ toString (take pr $ pref ++ cyc)
showD = show . unMod
toString = intercalate sep . map showD . reverse
ell = "…" ++ sep
sep
| radix n < 11 = ""
| otherwise = " "
instance Radix p prec => Eq (Q' p prec) where
a' == b' =
(isZero a && isZero b)
|| (valuation a == valuation b && unit a == unit b)
where
a = normalize a'
b = normalize b'
instance Radix p prec => Ord (Q' p prec) where
compare = error "Order is nor defined for p-adics."
instance Radix p prec => Num (Q' p prec) where
fromInteger n = normalize $ Q' (fromInteger n) 0
x@(Q' (Z' (R a)) va) + Q' (Z' (R b)) vb =
case compare va vb of
LT -> Q' (Z' (R (a + p ^% (vb - va) * b))) va
EQ -> Q' (Z' (R (a + b))) va
GT -> Q' (Z' (R (p ^% (va - vb) * a + b))) vb
where
p = fromInteger (radix x)
Q' (Z' (R a)) va * Q' (Z' (R b)) vb = Q' (Z' (R (a * b))) (va + vb)
negate (Q' u v) = Q' (negate u) v
abs = fromRational . norm
signum = pSignum
newtype Delay prec p = Delay (Q' p prec)
instance Radix p prec => Fractional (Q' p prec) where
fromRational 0 = 0
fromRational x = res
where
res = Q' (n `div` d) v
p = fromInteger $ natVal (Delay res)
(q, v) = getUnitQ p x
(n, d) = (mkUnit $ numerator q, mkUnit $ denominator q)
a / b = Q' u (v + valuation a - valuation b')
where
b' = normalize b
Q' u v = fromRational (lifted a % lifted b')
instance Radix p prec => Real (Q' p prec) where
toRational n = toRational (unit n) / norm n
pUndefinedError s = error $ s ++ " is undifined for p-adics."
fromEither = either error id
instance Radix p prec => Floating (Q' p prec) where
x ** y = fromEither $ pPow x y
exp = fromEither . pExp
log = fromEither . pLog
sinh = fromEither . pSinh
cosh = fromEither . pCosh
sin = fromEither . pSin
cos = fromEither . pCos
asinh = fromEither . pAsinh
acosh = fromEither . pCosh
atanh = fromEither . pTanh
asin = fromEither . pAsin
sqrt x = case pSqrt x of
res:_ -> res
[] -> error $ "sqrt: digit " ++ show (firstDigit x) ++ " is not a square residue!"
pi = pUndefinedError "pi"
acos = pUndefinedError "acos"
atan = pUndefinedError "atan"