packages feed

padic-0.1.0.0: src/Math/NumberTheory/Padic/Integer.hs

{-# OPTIONS_HADDOCK hide, prune, ignore-exports #-}

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FlexibleInstances #-}

module Math.NumberTheory.Padic.Integer  where

import Data.List (intercalate)
import Data.Mod
import Data.Word
import Data.Ratio
import GHC.TypeLits (Nat, natVal)
import GHC.Integer.GMP.Internals (recipModInteger)
import Math.NumberTheory.Padic.Types
import Math.NumberTheory.Padic.Analysis

------------------------------------------------------------

type instance Padic Integer p = Z' p (SufficientPrecision Word p)
type instance Padic Int p = Z' p (SufficientPrecision Int p)
type instance Padic Word8 p = Z' p (SufficientPrecision Word8 p)
type instance Padic Word16 p = Z' p (SufficientPrecision Word16 p)
type instance Padic Word32 p = Z' p (SufficientPrecision Word32 p)
type instance Padic Word64 p = Z' p (SufficientPrecision Word64 p)
type instance Padic Word p = Z' p (SufficientPrecision Word64 p)


-- |  Integer p-adic number (an element of \(\mathbb{Z}_p\)) with default precision.
type Z p = Z' p (SufficientPrecision Word32 p)

-- |  Integer p-adic number with explicitly specified precision.
newtype Z' (p :: Nat) (prec :: Nat) = Z' (R prec p)
newtype R (prec :: Nat ) (p :: Nat) = R (Mod (LiftedRadix p prec))

instance Radix p prec => Eq (Z' p prec) where
  x@(Z' (R a)) == Z' (R b) = unMod a `mod` pk == unMod b `mod` pk
    where
      pk = radix x ^ precision x

instance Radix p prec => PadicNum (Z' p prec) where
  type Unit (Z' p prec) = Z' p prec
  type Digit (Z' p prec) = Mod p 

  {-# INLINE precision #-}
  precision = fromIntegral . natVal

  {-# INLINE  radix #-}
  radix (Z' r) = fromIntegral $ natVal r
  
  {-# INLINE fromDigits #-}
  fromDigits = mkUnit . fromRadix

  {-# INLINE digits #-}
  digits n = toRadix (lifted n)

  {-# INLINE lifted #-}
  lifted (Z' (R n)) = lifted n

  {-# INLINE mkUnit #-}
  mkUnit = Z' . R . fromInteger

  {-# INLINE fromUnit #-}
  fromUnit (u, v) = mkUnit $ radix u ^ fromIntegral v * lifted u

  splitUnit n = case getUnitZ (radix n) (lifted n) of
                  (0, 0) -> (0, precision n)
                  (u, v) -> (mkUnit u, v)
  
  isInvertible n = (lifted n `mod` p) `gcd` p == 1
    where
      p = radix n
  
  inverse (Z' (R n))  = Z' . R <$> invertMod n

instance Radix p prec => Show (Z' p prec) where
  show n = 
     case findCycle pr ds of
       Nothing | length ds > pr -> ell ++ toString (take pr ds)
               | otherwise -> toString ds
       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)
    where
      pr = precision n
      ds = digits n
      showD = show . unMod
      toString = intercalate sep . map showD . reverse
      ell = "…" ++ sep 
      sep
        | radix n < 11 = ""
        | otherwise = " "

instance Radix p prec => Num (Z' p prec) where
  fromInteger = Z' . R . fromInteger
  Z' (R a) + Z' (R b) = Z' . R $ a + b
  Z' (R a) - Z' (R b) = Z' . R $ a - b
  Z' (R a) * Z' (R b) = Z' . R $ a * b
  negate (Z' (R a)) = Z' . R $ negate a
  abs x = if valuation x == 0 then 1 else 0
  signum = pSignum

instance Radix p prec  => Enum (Z' p prec) where
  toEnum = fromIntegral
  fromEnum = fromIntegral . toInteger

instance Radix p prec => Real (Z' p prec) where
  toRational 0 = 0
  toRational n = extEuclid (lifted n, liftedRadix n)

instance Radix p prec => Integral (Z' p prec) where
  toInteger n = if denominator r == 1
                then numerator r
                else lifted n `mod` (radix n ^ precision n)
    where
      r = toRational n
  a `quotRem` b = case inverse b of
    Nothing -> error $ show b ++ " is not divisible modulo " ++ show (radix a) ++ "!" 
    Just r -> let q = a*r in (q, a - q * b)


instance Radix p prec => Ord (Z' p prec) where
  compare = error "ordering is not defined for Z"

{-| Integer power function (analog of (^) operator ) -}
zPow :: Radix p prec => Z' p prec -> Z' p prec -> Z' p prec
zPow (Z' (R a)) (Z' (R b)) = Z' . R $  a ^% fromIntegral (unMod b)