packages feed

sloane-2.0.1: Sloane/GF.hs

-- |
-- Copyright   : Anders Claesson 2012-2015
-- Maintainer  : Anders Claesson <anders.claesson@gmail.com>
-- License     : BSD-3
--
module Sloane.GF
    ( Series (..)
    , GF
    , imap
    , diff
    , squareRoot
    , (^^^)
    , o
    , ogf
    , egf
    , ogfCoeffs
    , egfCoeffs
    )where

import Data.List
import Data.Maybe
import Data.Ratio

-- Reference: M. D. McIlroy, The music of streams,
-- Information Processing Letters 77 (2001) 189-195.

newtype Series a = Series { coeffs :: [a] } deriving (Show, Eq)

type GF = Series Rational

instance Functor Series where
    fmap f = Series . fmap f . coeffs

instance (Eq a, Num a) => Num (Series a) where
    (+) = lift2 add
    (*) = lift2 mul
    fromInteger c = Series [fromInteger c]
    negate = fmap negate
    signum = undefined
    abs    = undefined

instance (Eq a, Fractional a) => Fractional (Series a) where
     fromRational c = Series [fromRational c]
     (/) = lift2 divide

degree :: (Num a, Eq a) => Series a -> Int
degree (Series [0]) = -1
degree (Series cs)  = length cs - 1
-- degree (Series cs)  = length (dropWhile (==0) (reverse cs)) - 1

o :: (Eq a, Fractional a) => Series a -> Series a -> Series a
o = lift2 compose

lift :: ([a] -> [b]) -> Series a -> Series b
lift f (Series as) = Series (f as)

lift2 :: ([a] -> [b] -> [c]) -> Series a -> Series b -> Series c
lift2 op (Series as) (Series bs) = Series (as `op` bs)

add :: Num a => [a] -> [a] -> [a]
add []     ds     = ds
add cs     []     = cs
add (c:cs) (d:ds) = c+d : add cs ds

sub :: Num a => [a] -> [a] -> [a]
cs `sub` ds = cs `add` map negate ds

(!*!) :: (Eq a, Num a) => a -> a -> a
(!*!) _ 0 = 0
(!*!) 0 _ = 0
(!*!) a b = a*b

mul :: (Eq a, Num a) => [a] -> [a] -> [a]
mul (c:ct) ds@(d:dt) = c!*!d : map (c !*!) dt `add` (ct `mul` ds)
mul _      _         = []

divide :: (Eq a, Fractional a) => [a] -> [a] -> [a]
divide []     (0:_)     = undefined
divide []      _        = []
divide (0:ct) (0:dt)    = ct `divide` dt
divide (c:ct) ds@(d:dt) = q : (ct `sub` ([q] `mul` dt)) `divide` ds where q = c/d
divide _      []        = undefined

diff :: GF -> GF
diff = lift dF where {dF [] = []; dF (_:ct) = zipWith (*) [1..] ct}

nthRootApprox :: Integer -> GF -> [GF]
nthRootApprox n f@(Series (1:_)) =
    iterateUntilFixed (nthRootNext n f) (Series [1])
nthRootApprox _ _ = error "GF has constant term different from 1"

nthRootNext :: Integer -> GF -> GF -> GF
nthRootNext n f g = Series (take (1 + 2*degree f) ds)
  where
    Series ds = (Series [n%1-1] * g^n + f) / (Series [n%1] * g^(n-1))

iterateUntilFixed :: Eq a => (a -> a) -> a -> [a]
iterateUntilFixed f x = x : (if x == y then [] else ys)
  where
    ys@(y:_) = iterateUntilFixed f (f x)

saddlePoint :: Eq a => [a] -> Maybe a
saddlePoint [c] = Just c
saddlePoint cs  = listToMaybe [ c | (c,d) <- zip cs (drop 1 cs), c == d ]

nthRoot1 :: Integer -> GF -> GF
nthRoot1 n f@(Series (1:_)) =
    let css = transpose (map coeffs (nthRootApprox n f))
    in Series $ map fromJust (takeWhile isJust (map saddlePoint css))
nthRoot1 _ _ = error "GF has constant term different from 1"

nthRoot :: Integer -> GF -> GF
nthRoot _ (Series [])       = Series []
nthRoot n (Series cs@(c:_)) = Series [d] * nthRoot1 n (Series $ map (/c) cs)
    where
      d = toRational (fromRational c ** fromRational (1%n) :: Double)

(^^^) :: GF -> Rational -> GF
(^^^) f r = case (numerator r, denominator r) of
              (n, 1) -> f ^^ n
              (0, _) -> ogf [1::Int]
              (n, k) -> nthRoot k f ^^ n

-- XXX: Deprecate and use (^^^(1%2)) instead?
squareRoot :: GF -> GF
squareRoot = Series . map toRational . squareRoot' . map fromRational . coeffs

-- XXX: Deprecate?
squareRoot' :: [Double] -> [Double]
squareRoot' []     = []
squareRoot' (c:ct) = ds
  where
    ds = d : ct `divide` ([d] `add` ds)
    d  = sqrt c

compose :: (Eq a, Fractional a) => [a] -> [a] -> [a]
[]     `compose` _         = []
(c:_)  `compose` []        = [c]
(c:ct) `compose` ds@(0:dt) = c : dt `mul` (ct `compose` ds)
(c:ct) `compose` ds        = [c] `add` ds `mul` (ct `compose` ds)
                                              -- ct must be finite

imap :: (Integer -> a -> b) -> Series a -> Series b
imap f = Series . zipWith f [0..] . coeffs

factorial :: Integer -> Integer
factorial n = product [1..n]

ogf :: Real a => [a] -> GF
ogf = Series . map toRational

ogfCoeffs :: GF -> [Rational]
ogfCoeffs = coeffs

egf :: Real a => [a] -> GF
egf = Series . zipWith (\n c -> toRational c * (1 % factorial n)) [0..]

egfCoeffs :: GF -> [Rational]
egfCoeffs = ogfCoeffs . imap (\i -> (* (factorial i % 1)))