{-# LANGUAGE NoImplicitPrelude #-}
module Number.Positional where
import qualified MathObj.LaurentPolynomial as LPoly
import qualified MathObj.Polynomial.Core as Poly
import qualified Algebra.IntegralDomain as Integral
import qualified Algebra.Ring as Ring
import qualified Algebra.ToInteger as ToInteger
import qualified Prelude as P98
import qualified NumericPrelude.Numeric as NP
import NumericPrelude.Base
import NumericPrelude.Numeric hiding (sqrt, tan, one, zero, )
import qualified Data.List as List
import Data.Char (intToDigit)
import qualified Data.List.Match as Match
import Data.Function.HT (powerAssociative, nest, )
import Data.Tuple.HT (swap, )
import Data.Maybe.HT (toMaybe, )
import Data.Bool.HT (select, if', )
import NumericPrelude.List (mapLast, )
import Data.List.HT
(sliceVertical, mapAdjacent,
padLeft, padRight, )
type T = (Exponent, Mantissa)
type FixedPoint = (Integer, Mantissa)
type Mantissa = [Digit]
type Digit = Int
type Exponent = Int
type Basis = Int
moveToZero :: Basis -> Digit -> (Digit,Digit)
moveToZero b n =
let b2 = div b 2
(q,r) = divMod (n+b2) b
in (q,r-b2)
checkPosDigit :: Basis -> Digit -> Digit
checkPosDigit b d =
if d>=0 && d<b
then d
else error ("digit " ++ show d ++ " out of range [0," ++ show b ++ ")")
checkDigit :: Basis -> Digit -> Digit
checkDigit b d =
if abs d < b
then d
else error ("digit " ++ show d ++ " out of range ("
++ show (-b) ++ "," ++ show b ++ ")")
nonNegative :: Basis -> T -> T
nonNegative b x =
let (xe,xm) = compress b x
in (xe, nonNegativeMant b xm)
nonNegativeMant :: Basis -> Mantissa -> Mantissa
nonNegativeMant b =
let recurse (x0:x1:xs) =
select (replaceZeroChain x0 (x1:xs))
[(x1 >= 1, x0 : recurse (x1:xs)),
(x1 <= -1, (x0-1) : recurse ((x1+b):xs))]
recurse xs = xs
replaceZeroChain x xs =
let (xZeros,xRem) = span (0==) xs
in case xRem of
[] -> (x:xs)
(y:ys) ->
if y>=0
then x : Match.replicate xZeros 0 ++ recurse xRem
else (x-1) : Match.replicate xZeros (b-1) ++ recurse ((y+b) : ys)
in recurse
compress :: Basis -> T -> T
compress _ x@(_, []) = x
compress b (xe, xm) =
let (hi:his,los) = unzip (map (moveToZero b) xm)
in prependDigit hi (xe, Poly.add his los)
compressFirst :: Basis -> T -> T
compressFirst _ x@(_, []) = x
compressFirst b (xe, x:xs) =
let (hi,lo) = moveToZero b x
in prependDigit hi (xe, lo:xs)
compressMant :: Basis -> Mantissa -> Mantissa
compressMant _ [] = []
compressMant b (x:xs) =
let (his,los) = unzip (map (moveToZero b) xs)
in Poly.add his (x:los)
compressSecondMant :: Basis -> Mantissa -> Mantissa
compressSecondMant b (x0:x1:xs) =
let (hi,lo) = moveToZero b x1
in x0+hi : lo : xs
compressSecondMant _ xm = xm
prependDigit :: Basis -> T -> T
prependDigit 0 x = x
prependDigit x (xe, xs) = (xe+1, x:xs)
trim :: T -> T
trim (xe,xm) =
let (xZero, xNonZero) = span (0 ==) xm
in (xe - length xZero, xNonZero)
trimUntil :: Exponent -> T -> T
trimUntil e =
until (\(xe,xm) -> xe<=e ||
not (null xm || head xm == 0)) trimOnce
trimOnce :: T -> T
trimOnce (xe,[]) = (xe-1,[])
trimOnce (xe,0:xm) = (xe-1,xm)
trimOnce x = x
decreaseExp :: Basis -> T -> T
decreaseExp b (xe,xm) =
(xe-1, pumpFirst b xm)
pumpFirst :: Basis -> Mantissa -> Mantissa
pumpFirst b xm =
case xm of
(x0:x1:xs) -> x0*b+x1:xs
(x0:[]) -> x0*b:[]
[] -> []
decreaseExpFP :: Basis -> (Exponent, FixedPoint) ->
(Exponent, FixedPoint)
decreaseExpFP b (xe,xm) =
(xe-1, pumpFirstFP b xm)
pumpFirstFP :: Basis -> FixedPoint -> FixedPoint
pumpFirstFP b (x,xm) =
let xb = x * fromIntegral b
in case xm of
(x0:xs) -> (xb + fromIntegral x0, xs)
[] -> (xb, [])
negativeExp :: Basis -> T -> T
negativeExp b x =
let tx = trimUntil (-10) x
in if fst tx >=0 then decreaseExp b tx else tx
fromBaseCardinal :: Basis -> Integer -> T
fromBaseCardinal b n =
let mant = mantissaFromCard b n
in (length mant - 1, mant)
fromBaseInteger :: Basis -> Integer -> T
fromBaseInteger b n =
if n<0
then neg b (fromBaseCardinal b (negate n))
else fromBaseCardinal b n
mantissaToNum :: Ring.C a => Basis -> Mantissa -> a
mantissaToNum bInt =
let b = fromIntegral bInt
in foldl (\x d -> x*b + fromIntegral d) 0
mantissaFromCard :: (ToInteger.C a) => Basis -> a -> Mantissa
mantissaFromCard bInt n =
let b = NP.fromIntegral bInt
in reverse (map NP.fromIntegral
(Integral.decomposeVarPositional (repeat b) n))
mantissaFromInt :: (ToInteger.C a) => Basis -> a -> Mantissa
mantissaFromInt b n =
if n<0
then negate (mantissaFromCard b (negate n))
else mantissaFromCard b n
mantissaFromFixedInt :: Basis -> Exponent -> Integer -> Mantissa
mantissaFromFixedInt bInt e n =
let b = NP.fromIntegral bInt
in map NP.fromIntegral (uncurry (:) (List.mapAccumR
(\x () -> divMod x b)
n (replicate (pred e) ())))
fromBaseRational :: Basis -> Rational -> T
fromBaseRational bInt x =
let b = NP.fromIntegral bInt
xDen = denominator x
(xInt,xNum) = divMod (numerator x) xDen
(xe,xm) = fromBaseInteger bInt xInt
xFrac = List.unfoldr
(\num -> toMaybe (num/=0) (divMod (num*b) xDen)) xNum
in (xe, xm ++ map NP.fromInteger xFrac)
toFixedPoint :: Basis -> T -> FixedPoint
toFixedPoint b (xe,xm) =
if xe>=0
then let (x0,x1) = splitAtPadZero (xe+1) xm
in (mantissaToNum b x0, x1)
else (0, replicate (- succ xe) 0 ++ xm)
fromFixedPoint :: Basis -> FixedPoint -> T
fromFixedPoint b (xInt,xFrac) =
let (xe,xm) = fromBaseInteger b xInt
in (xe, xm++xFrac)
toDouble :: Basis -> T -> Double
toDouble b (xe,xm) =
let txm = take (mantLengthDouble b) xm
bf = fromIntegral b
br = recip bf
in fieldPower xe bf * foldr (\xi y -> fromIntegral xi + y*br) 0 txm
fromDouble :: Basis -> Double -> T
fromDouble b x =
let (n,frac) = splitFraction x
(mant,e) = P98.decodeFloat frac
fracR = alignMant b (-1)
(fromBaseRational b (mant % ringPower (-e) 2))
in fromFixedPoint b (n, fracR)
fromDoubleApprox :: Basis -> Double -> T
fromDoubleApprox b x =
let (xe,xm) = fromDouble b x
in (xe, take (mantLengthDouble b) xm)
fromDoubleRough :: Basis -> Double -> T
fromDoubleRough b x =
let (xe,xm) = fromDouble b x
in (xe, take 2 xm)
mantLengthDouble :: Basis -> Exponent
mantLengthDouble b =
let fi = fromIntegral :: Int -> Double
x = undefined :: Double
in ceiling
(logBase (fi b) (fromInteger (P98.floatRadix x)) *
fi (P98.floatDigits x))
liftDoubleApprox :: Basis -> (Double -> Double) -> T -> T
liftDoubleApprox b f = fromDoubleApprox b . f . toDouble b
liftDoubleRough :: Basis -> (Double -> Double) -> T -> T
liftDoubleRough b f = fromDoubleRough b . f . toDouble b
showDec :: Exponent -> T -> String
showDec = showBasis 10
showHex :: Exponent -> T -> String
showHex = showBasis 16
showBin :: Exponent -> T -> String
showBin = showBasis 2
showBasis :: Basis -> Exponent -> T -> String
showBasis b e xBig =
let x = rootBasis b e xBig
(sign,absX) =
case cmp b x (fst x,[]) of
LT -> ("-", neg b x)
_ -> ("", x)
(int, frac) = toFixedPoint b (nonNegative b absX)
checkedFrac = map (checkPosDigit b) frac
intStr =
if b==10
then show int
else map intToDigit (mantissaFromInt b int)
in sign ++ intStr ++ '.' : map intToDigit checkedFrac
powerBasis :: Basis -> Exponent -> T -> T
powerBasis b e (xe,xm) =
let (ye,r) = divMod xe e
(y0,y1) = splitAtPadZero (r+1) xm
y1pad = mapLast (padRight 0 e) (sliceVertical e y1)
in (ye, map (mantissaToNum b) (y0 : y1pad))
rootBasis :: Basis -> Exponent -> T -> T
rootBasis b e (xe,xm) =
let splitDigit d = padLeft 0 e (mantissaFromInt b d)
in nest (e-1) trimOnce
((xe+1)*e-1, concatMap splitDigit (map (checkDigit (ringPower e b)) xm))
fromBasis :: Basis -> Basis -> T -> T
fromBasis bDst bSrc x =
let (int,frac) = toFixedPoint bSrc x
in fromFixedPoint bDst (int, fromBasisMant bDst bSrc frac)
fromBasisMant :: Basis -> Basis -> Mantissa -> Mantissa
fromBasisMant _ _ [] = []
fromBasisMant bDst bSrc xm =
let
inc = ceiling
(logBase (fromIntegral bSrc) (fromIntegral bDst)
* unit * 1.1 :: Double)
unit :: Ring.C a => a
unit = 10000
cmpr (mag,xs) = (mag - unit, compressMant bSrc xs)
scl (_,[]) = Nothing
scl (mag,xs) =
let (newMag,y:ys) =
until ((<unit) . fst) cmpr
(mag + inc, Poly.scale bDst xs)
(d,y0) = moveToZero bSrc y
in Just (d, (newMag, y0:ys))
in List.unfoldr scl (0::Int,xm)
cmp :: Basis -> T -> T -> Ordering
cmp b x y =
let (_,dm) = sub b x y
recurse [] = EQ
recurse (d:[]) = compare d 0
recurse (d0:d1:ds) =
select (recurse (d0*b+d1 : ds))
[(d0 < -2, LT),
(d0 > 2, GT)]
in recurse dm
lessApprox :: Basis -> Exponent -> T -> T -> Bool
lessApprox b bnd x y =
let tx = trunc bnd x
ty = trunc bnd y
in LT == cmp b (liftLaurent2 LPoly.add (bnd,[2]) tx) ty
trunc :: Exponent -> T -> T
trunc bnd (xe, xm) =
if bnd > xe
then (bnd, [])
else (xe, take (1+xe-bnd) xm)
equalApprox :: Basis -> Exponent -> T -> T -> Bool
equalApprox b bnd x y =
fst (trimUntil bnd (sub b x y)) == bnd
ifLazy :: Basis -> Bool -> T -> T -> T
ifLazy b c x@(xe, _) y@(ye, _) =
let ze = max xe ye
xm = alignMant b ze x
ym = alignMant b ze y
recurse :: Mantissa -> Mantissa -> Mantissa
recurse xs0 ys0 =
withTwoMantissas xs0 ys0 [] $ \(x0,xs1) (y0,ys1) ->
if abs (y0-x0) > 2
then if c then xs0 else ys0
else
withTwoMantissas xs1 ys1 ((if x0==y0 || c then x0 else y0) : []) $
\(x1,xs2) (y1,ys2) ->
let z0 = mean2 b (x0,x1) (y0,y1)
x1' = x1+(x0-z0)*b
y1' = y1+(y0-z0)*b
in if abs x1' < b && abs y1' < b
then z0 : recurse (x1':xs2) (y1':ys2)
else if c then xs0 else ys0
in (ze, recurse xm ym)
{-# INLINE mean2 #-}
mean2 :: Basis -> (Digit,Digit) -> (Digit,Digit) -> Digit
mean2 b (x0,x1) (y0,y1) =
((x0+y0+1)*b + (x1+y1)) `div` (2*b)
withTwoMantissas ::
Mantissa -> Mantissa ->
a ->
((Digit,Mantissa) -> (Digit,Mantissa) -> a) ->
a
withTwoMantissas [] [] r _ = r
withTwoMantissas [] (y:ys) _ f = f (0,[]) (y,ys)
withTwoMantissas (x:xs) [] _ f = f (x,xs) (0,[])
withTwoMantissas (x:xs) (y:ys) _ f = f (x,xs) (y,ys)
align :: Basis -> Exponent -> T -> T
align b ye x = (ye, alignMant b ye x)
alignMant :: Basis -> Exponent -> T -> Mantissa
alignMant b e (xe,xm) =
if e>=xe
then replicate (e-xe) 0 ++ xm
else let (xm0,xm1) = splitAtPadZero (xe-e+1) xm
in mantissaToNum b xm0 : xm1
absolute :: T -> T
absolute (xe,xm) = (xe, absMant xm)
absMant :: Mantissa -> Mantissa
absMant xa@(x:xs) =
case compare x 0 of
EQ -> x : absMant xs
LT -> Poly.negate xa
GT -> xa
absMant [] = []
fromLaurent :: LPoly.T Digit -> T
fromLaurent (LPoly.Cons nxe xm) = (NP.negate nxe, xm)
toLaurent :: T -> LPoly.T Digit
toLaurent (xe, xm) = LPoly.Cons (NP.negate xe) xm
liftLaurent2 ::
(LPoly.T Digit -> LPoly.T Digit -> LPoly.T Digit) ->
(T -> T -> T)
liftLaurent2 f x y =
fromLaurent (f (toLaurent x) (toLaurent y))
liftLaurentMany ::
([LPoly.T Digit] -> LPoly.T Digit) ->
([T] -> T)
liftLaurentMany f =
fromLaurent . f . map toLaurent
add :: Basis -> T -> T -> T
add b x y = compress b (liftLaurent2 LPoly.add x y)
sub :: Basis -> T -> T -> T
sub b x y = compress b (liftLaurent2 LPoly.sub x y)
neg :: Basis -> T -> T
neg _ (xe, xm) = (xe, Poly.negate xm)
addSome :: Basis -> [T] -> T
addSome b = compress b . liftLaurentMany sum
addMany :: Basis -> [T] -> T
addMany _ [] = zero
addMany b ys =
let recurse xs =
case map (addSome b) (sliceVertical b xs) of
[s] -> s
sums -> recurse sums
in recurse ys
type Series = [(Exponent, T)]
series :: Basis -> Series -> T
series _ [] = error "empty series: don't know a good exponent"
series b summands =
let (es,xs) = unzip summands
safeSeries = zip (scanl1 min es) xs
in seriesPlain b safeSeries
seriesPlain :: Basis -> Series -> T
seriesPlain _ [] = error "empty series: don't know a good exponent"
seriesPlain b summands =
let (es,m:ms) = unzip (map (uncurry (align b)) summands)
eDifs = mapAdjacent (-) es
eDifLists = sliceVertical (pred b) eDifs
mLists = sliceVertical (pred b) ms
accum sumM (eDifList,mList) =
let subM = LPoly.addShiftedMany eDifList (sumM:mList)
len = concatMap (flip replicate ()) eDifList
(high,low) = splitAtMatchPadZero len subM
in (compressMant b low, high)
(trailingDigits, chunks) =
List.mapAccumL accum m (zip eDifLists mLists)
in compress b (head es, concat chunks ++ trailingDigits)
splitAtPadZero :: Int -> Mantissa -> (Mantissa, Mantissa)
splitAtPadZero n [] = (replicate n 0, [])
splitAtPadZero 0 xs = ([], xs)
splitAtPadZero n (x:xs) =
let (ys, zs) = splitAtPadZero (n-1) xs
in (x:ys, zs)
splitAtMatchPadZero :: [()] -> Mantissa -> (Mantissa, Mantissa)
splitAtMatchPadZero n [] = (Match.replicate n 0, [])
splitAtMatchPadZero [] xs = ([], xs)
splitAtMatchPadZero n (x:xs) =
let (ys, zs) = splitAtMatchPadZero (tail n) xs
in (x:ys, zs)
truncSeriesSummands :: Series -> Series
truncSeriesSummands = map (\(e,x) -> (e,trunc (-20) x))
scale :: Basis -> Digit -> T -> T
scale b y x = compress b (scaleSimple y x)
scaleSimple :: Basis -> T -> T
scaleSimple y (xe, xm) = (xe, Poly.scale y xm)
scaleMant :: Basis -> Digit -> Mantissa -> Mantissa
scaleMant b y xm = compressMant b (Poly.scale y xm)
mulSeries :: Basis -> T -> T -> Series
mulSeries _ (xe,[]) (ye,_) = [(xe+ye, zero)]
mulSeries b (xe,xm) (ye,ym) =
let zes = iterate pred (xe+ye+1)
zs = zipWith (\xd e -> scale b xd (e,ym)) xm (tail zes)
in zip zes zs
mul :: Basis -> T -> T -> T
mul b x y = trimOnce (seriesPlain b (mulSeries b x y))
divide :: Basis -> T -> T -> T
divide b (xe,xm) (ye',ym') =
let (ye,ym) = until ((>=b) . abs . head . snd)
(decreaseExp b)
(ye',ym')
in if null xm
then (xe,xm)
else nest 3 trimOnce (compress b (xe-ye, divMant b ym xm))
divMant :: Basis -> Mantissa -> Mantissa -> Mantissa
divMant _ [] _ = error "Number.Positional: division by zero"
divMant b ym xm0 =
snd $
List.mapAccumL
(\xm fullCompress ->
let z = div (head xm) (head ym)
dif = pumpFirst b (Poly.sub xm (scaleMant b z ym))
cDif = if fullCompress
then compressMant b dif
else compressSecondMant b dif
in (cDif, z))
xm0 (cycle (replicate (b-1) False ++ [True]))
divMantSlow :: Basis -> Mantissa -> Mantissa -> Mantissa
divMantSlow _ [] = error "Number.Positional: division by zero"
divMantSlow b ym =
List.unfoldr
(\xm ->
let z = div (head xm) (head ym)
d = compressMant b (pumpFirst b
(Poly.sub xm (Poly.scale z ym)))
in Just (z, d))
reciprocal :: Basis -> T -> T
reciprocal b = divide b one
divIntMant :: Basis -> Digit -> Mantissa -> Mantissa
divIntMant b y xInit =
List.unfoldr (\(r,rxs) ->
let rb = r*b
(rbx, xs', run) =
case rxs of
[] -> (rb, [], r/=0)
(x:xs) -> (rb+x, xs, True)
(d,m) = divMod rbx y
in toMaybe run (d, (m, xs')))
(0,xInit)
divIntMantInf :: Basis -> Digit -> Mantissa -> Mantissa
divIntMantInf b y =
map fst . tail .
scanl (\(_,r) x -> divMod (r*b+x) y) (undefined,0) .
(++ repeat 0)
divInt :: Basis -> Digit -> T -> T
divInt b y (xe,xm) =
let z = (xe, divIntMant b y xm)
tz = until ((<=1) . fst) (\(yi,zi) -> (div yi b, trimOnce zi)) (y,z)
in snd tz
sqrt :: Basis -> T -> T
sqrt b = sqrtDriver b sqrtFP
sqrtDriver :: Basis -> (Basis -> FixedPoint -> Mantissa) -> T -> T
sqrtDriver _ _ (xe,[]) = (div xe 2, [])
sqrtDriver b sqrtFPworker x =
let (exe,ex0:exm) = if odd (fst x) then decreaseExp b x else x
(nxe,(nx0,nxm)) =
until (\xi -> fst (snd xi) >= fromIntegral b ^ 2)
(nest 2 (decreaseExpFP b))
(exe, (fromIntegral ex0, exm))
in compress b (div nxe 2, sqrtFPworker b (nx0,nxm))
sqrtMant :: Basis -> Mantissa -> Mantissa
sqrtMant _ [] = []
sqrtMant b (x:xs) =
sqrtFP b (fromIntegral x, xs)
sqrtFP :: Basis -> FixedPoint -> Mantissa
sqrtFP b (x0,xs) =
let y0 = round (NP.sqrt (fromInteger x0 :: Double))
dyx0 = fromInteger (x0 - fromIntegral y0 ^ 2)
accum dif (e,ty) =
let yj = div (head dif + y0) (2*y0)
newDif = pumpFirst b $
LPoly.addShifted e
(Poly.sub dif (scaleMant b (2*yj) ty))
[yj*yj]
cNewDif =
if mod e b == 0
then compressMant b newDif
else compressSecondMant b newDif
in (cNewDif, yj)
truncs = lazyInits y
y = y0 : snd (List.mapAccumL
accum
(pumpFirst b (dyx0 : xs))
(zip [1..] (drop 2 truncs)))
in y
sqrtNewton :: Basis -> T -> T
sqrtNewton b = sqrtDriver b sqrtFPNewton
sqrtFPNewton :: Basis -> FixedPoint -> Mantissa
sqrtFPNewton bInt (x0,xs) =
let b = fromIntegral bInt
chunkLengths = iterate (2*) 1
xChunks = map (mantissaToNum bInt) $ snd $
List.mapAccumL (\x cl -> swap (splitAtPadZero cl x))
xs chunkLengths
basisPowers = iterate (^2) b
truncXs = scanl (\acc (bp,frac) -> acc*bp+frac) x0
(zip basisPowers xChunks)
accum y (bp, x) =
let ybp = y * bp
newY = div (ybp + div (x * div bp b) y) 2
in (newY, newY-ybp)
y0 = round (NP.sqrt (fromInteger x0 :: Double))
yChunks = snd $ List.mapAccumL accum
y0 (zip basisPowers (tail truncXs))
yFrac = concat $ zipWith (mantissaFromFixedInt bInt) chunkLengths yChunks
in fromInteger y0 : yFrac
lazyInits :: [a] -> [[a]]
lazyInits ~(x:xs) = [] : map (x:) (lazyInits xs)
expSeries :: Basis -> T -> Series
expSeries b xOrig =
let x = negativeExp b xOrig
xps = scanl (\p n -> divInt b n (mul b x p)) one [1..]
in map (\xp -> (fst xp, xp)) xps
expSmall :: Basis -> T -> T
expSmall b x = series b (expSeries b x)
expSeriesLazy :: Basis -> T -> Series
expSeriesLazy b x@(xe,_) =
let xps = scanl (\p n -> divInt b n (mul b x p)) one [1..]
es :: [Double]
es = zipWith (-)
(map fromIntegral (iterate ((1+xe)+) 0))
(scanl (+) 0
(map (logBase (fromIntegral b)
. fromInteger) [1..]))
in zip (map ceiling es) xps
expSmallLazy :: Basis -> T -> T
expSmallLazy b x = series b (expSeriesLazy b x)
exp :: Basis -> T -> T
exp b x =
let (xInt,xFrac) = toFixedPoint b (compress b x)
yFrac = expSmall b (-1,xFrac)
in intPower b xInt yFrac (recipEConst b) (eConst b)
intPower :: Basis -> Integer -> T -> T -> T -> T
intPower b expon neutral recipX x =
if expon >= 0
then cardPower b expon neutral x
else cardPower b (-expon) neutral recipX
cardPower :: Basis -> Integer -> T -> T -> T
cardPower b expon neutral x =
if expon >= 0
then powerAssociative (mul b) neutral x expon
else error "negative exponent - use intPower"
powerSeries :: Basis -> Rational -> T -> Series
powerSeries b expon xOrig =
let scaleRat ni yi =
divInt b (fromInteger (denominator yi) * ni) .
scaleSimple (fromInteger (numerator yi))
x = negativeExp b (sub b xOrig one)
xps = scanl (\p fac -> uncurry scaleRat fac (mul b x p))
one (zip [1..] (iterate (subtract 1) expon))
in map (\xp -> (fst xp, xp)) xps
powerSmall :: Basis -> Rational -> T -> T
powerSmall b y x = series b (powerSeries b y x)
power :: Basis -> Rational -> T -> T
power b expon x =
let num = numerator expon
den = denominator expon
rootX = root b den x
in intPower b num one (reciprocal b rootX) rootX
root :: Basis -> Integer -> T -> T
root b expon x =
let estimate = liftDoubleApprox b (** (1 / fromInteger expon)) x
estPower = cardPower b expon one estimate
residue = divide b x estPower
in mul b estimate (powerSmall b (1 % fromIntegral expon) residue)
cosSinhSmall :: Basis -> T -> (T, T)
cosSinhSmall b x =
let (coshXps, sinhXps) = unzip (sliceVertPair (expSeries b x))
in (series b coshXps,
series b sinhXps)
cosSinSmall :: Basis -> T -> (T, T)
cosSinSmall b x =
let (coshXps, sinhXps) = unzip (sliceVertPair (expSeries b x))
alternate s =
zipWith3 if' (cycle [True,False])
s (map (\(e,y) -> (e, neg b y)) s)
in (series b (alternate coshXps),
series b (alternate sinhXps))
cosSinFourth :: Basis -> T -> (T, T)
cosSinFourth b x =
let (cosx, sinx) = cosSinSmall b (divInt b 4 x)
sinx2 = mul b sinx sinx
cosx2 = mul b cosx cosx
sincosx = mul b sinx cosx
in (add b one (scale b 8 (mul b cosx2 (sub b cosx2 one))),
scale b 4 (mul b sincosx (sub b one (scale b 2 sinx2))))
cosSin :: Basis -> T -> (T, T)
cosSin b x =
let pi2 = divInt b 2 (piConst b)
(quadrant, frac) = toFixedPoint b (compress b (divide b x pi2))
wrapped = if quadrant==0 then x else mul b pi2 (-1, frac)
(cosW,sinW) = cosSinSmall b wrapped
in case mod quadrant 4 of
0 -> ( cosW, sinW)
1 -> (neg b sinW, cosW)
2 -> (neg b cosW, neg b sinW)
3 -> ( sinW, neg b cosW)
_ -> error "error in implementation of 'mod'"
tan :: Basis -> T -> T
tan b x = uncurry (flip (divide b)) (cosSin b x)
cot :: Basis -> T -> T
cot b x = uncurry (divide b) (cosSin b x)
lnSeries :: Basis -> T -> Series
lnSeries b xOrig =
let x = negativeExp b (sub b xOrig one)
mx = neg b x
xps = zipWith (divInt b) [1..] (iterate (mul b mx) x)
in map (\xp -> (fst xp, xp)) xps
lnSmall :: Basis -> T -> T
lnSmall b x = series b (lnSeries b x)
lnNewton :: Basis -> T -> T
lnNewton b y =
let estimate = liftDoubleApprox b log y
expRes = mul b y (expSmall b (neg b estimate))
residue =
sub b (mul b expRes (expSmallLazy b (neg b resTrim))) one
resTrim =
align b (- mantLengthDouble b) residue
lazyAdd (xe,xm) (ye,ym) =
(xe, LPoly.addShifted (xe-ye) xm ym)
x = lazyAdd estimate resTrim
in x
lnNewton' :: Basis -> T -> T
lnNewton' b y =
let estimate = liftDoubleApprox b log y
residue =
sub b (mul b y (expSmall b (neg b x))) one
resTrim =
align b (- mantLengthDouble b) residue
lazyAdd (xe,xm) (ye,ym) =
(xe, LPoly.addShifted (xe-ye) xm ym)
x = lazyAdd estimate resTrim
in x
ln :: Basis -> T -> T
ln b x@(xe,_) =
let e = round (log (fromIntegral b) * fromIntegral xe :: Double)
ei = fromIntegral e
y = trim $
if e<0
then powerAssociative (mul b) x (eConst b) (-ei)
else powerAssociative (mul b) x (recipEConst b) ei
estimate = liftDoubleApprox b log y
residue = mul b (expSmall b (neg b estimate)) y
in addSome b [(0,[e]), estimate, lnSmall b residue]
angle :: Basis -> (T,T) -> T
angle b (cosx, sinx) =
let wd = atan2 (toDouble b sinx) (toDouble b cosx)
wApprox = fromDoubleApprox b wd
(cosApprox, sinApprox) = cosSin b wApprox
(cosD,sinD) =
(add b (mul b cosx cosApprox)
(mul b sinx sinApprox),
sub b (mul b sinx cosApprox)
(mul b cosx sinApprox))
sinDSmall = negativeExp b sinD
in add b wApprox (arctanSmall b (divide b sinDSmall cosD))
arctanSeries :: Basis -> T -> Series
arctanSeries b xOrig =
let x = negativeExp b xOrig
mx2 = neg b (mul b x x)
xps = zipWith (divInt b) [1,3..] (iterate (mul b mx2) x)
in map (\xp -> (fst xp, xp)) xps
arctanSmall :: Basis -> T -> T
arctanSmall b x = series b (arctanSeries b x)
arctanStem :: Basis -> Digit -> T
arctanStem b n =
let x = (0, divIntMant b n [1])
divN2 = divInt b n . divInt b (-n)
xps = zipWith (divInt b) [1,3..] (iterate (trim . divN2) x)
in series b (map (\xp -> (fst xp, xp)) xps)
arctan :: Basis -> T -> T
arctan b x =
let estimate = liftDoubleRough b atan x
tanEst = tan b estimate
residue = divide b (sub b x tanEst) (add b one (mul b x tanEst))
in addSome b [estimate, arctanSmall b residue]
arctanClassic :: Basis -> T -> T
arctanClassic b x =
let absX = absolute x
pi2 = divInt b 2 (piConst b)
in select
(divInt b 2 (sub b pi2
(arctanSmall b
(divInt b 2 (sub b (reciprocal b x) x)))))
[(lessApprox b (-5) absX (fromBaseRational b (11%19)),
arctanSmall b x),
(lessApprox b (-5) (fromBaseRational b (19%11)) absX,
sub b pi2 (arctanSmall b (reciprocal b x)))]
zero :: T
zero = (0,[])
one :: T
one = (0,[1])
minusOne :: T
minusOne = (0,[-1])
eConst :: Basis -> T
eConst b = expSmall b one
recipEConst :: Basis -> T
recipEConst b = expSmall b minusOne
piConst :: Basis -> T
piConst b =
let numCompress = takeWhile (0/=)
(iterate (flip div b) (4*(44+7+12+24)))
stArcTan k den = scaleSimple k (arctanStem b den)
sum' = addSome b
[stArcTan 44 57,
stArcTan 7 239,
stArcTan (-12) 682,
stArcTan 24 12943]
in foldl (const . compress b)
(scaleSimple 4 sum') numCompress
sliceVertPair :: [a] -> [(a,a)]
sliceVertPair (x0:x1:xs) = (x0,x1) : sliceVertPair xs
sliceVertPair [] = []
sliceVertPair _ = error "odd number of elements"