packages feed

cf 0.1 → 0.2

raw patch · 5 files changed

+725/−180 lines, 5 filesdep +QuickCheckdep +cfdep +test-frameworkdep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: QuickCheck, cf, test-framework, test-framework-quickcheck2, test-framework-th

Dependency ranges changed: base

API changes (from Hackage documentation)

- Math.ContinuedFraction: convergents :: CF -> [Rational]
- Math.ContinuedFraction: data CF
- Math.ContinuedFraction: digits :: Integer -> CF -> [Integer]
- Math.ContinuedFraction: exp1 :: CF
- Math.ContinuedFraction: instance Enum CF
- Math.ContinuedFraction: showCF :: CF -> String
- Math.ContinuedFraction: sqrt2 :: CF
+ Math.ContinuedFraction: cfString :: CF -> String
+ Math.ContinuedFraction: cfcf :: CF' CF -> CF
+ Math.ContinuedFraction: instance Floating CF
+ Math.ContinuedFraction: instance HasFractionField CF
+ Math.ContinuedFraction: instance HasFractionField Integer
+ Math.ContinuedFraction: instance HasFractionField Rational
+ Math.ContinuedFraction: type CF = CF' Integer
+ Math.ContinuedFraction.Interval: Finite :: a -> Extended a
+ Math.ContinuedFraction.Interval: Infinity :: Extended a
+ Math.ContinuedFraction.Interval: Interval :: (Extended a) -> (Extended a) -> Interval a
+ Math.ContinuedFraction.Interval: comparePosition :: Interval Rational -> Interval Rational -> Maybe Ordering
+ Math.ContinuedFraction.Interval: data Extended a
+ Math.ContinuedFraction.Interval: data Interval a
+ Math.ContinuedFraction.Interval: elementOf :: Ord a => Extended a -> Interval a -> Bool
+ Math.ContinuedFraction.Interval: epsilon :: Rational
+ Math.ContinuedFraction.Interval: instance Eq a => Eq (Extended a)
+ Math.ContinuedFraction.Interval: instance Eq a => Eq (Interval a)
+ Math.ContinuedFraction.Interval: instance Num a => Num (Extended a)
+ Math.ContinuedFraction.Interval: instance Show (Interval Rational)
+ Math.ContinuedFraction.Interval: instance Show a => Show (Extended a)
+ Math.ContinuedFraction.Interval: intervalDigit :: RealFrac a => Interval a -> Maybe Integer
+ Math.ContinuedFraction.Interval: mergeInterval :: Ord a => Interval a -> Interval a -> Interval a
+ Math.ContinuedFraction.Interval: smallerThan :: (Num a, Ord a) => Interval a -> Interval a -> Bool
+ Math.ContinuedFraction.Interval: subset :: Ord a => Interval a -> Interval a -> Bool
+ Math.ContinuedFraction.Simple: data CF
+ Math.ContinuedFraction.Simple: digits :: CF -> [Integer]
+ Math.ContinuedFraction.Simple: exp1 :: CF
+ Math.ContinuedFraction.Simple: instance Eq CF
+ Math.ContinuedFraction.Simple: instance Fractional CF
+ Math.ContinuedFraction.Simple: instance Num CF
+ Math.ContinuedFraction.Simple: instance Ord CF
+ Math.ContinuedFraction.Simple: instance Real CF
+ Math.ContinuedFraction.Simple: instance RealFrac CF
+ Math.ContinuedFraction.Simple: instance Show CF
+ Math.ContinuedFraction.Simple: showCF :: CF -> String
+ Math.ContinuedFraction.Simple: sqrt2 :: CF

Files

cf.cabal view
@@ -1,6 +1,6 @@ name:                cf-version:             0.1-synopsis:            Infinite precision arithmetic using continued fractions+version:             0.2+synopsis:            Exact real arithmetic using continued fractions license:             MIT    license-file:        LICENSE author:              Mitchell Riley@@ -15,7 +15,23 @@   location: git://github.com/mvr/cf.git  library-  hs-source-dirs:      src-  exposed-modules:     Math.ContinuedFraction+  hs-source-dirs:  src+  exposed-modules: Math.ContinuedFraction,+                   Math.ContinuedFraction.Simple,+                   Math.ContinuedFraction.Interval   build-depends:       base >=4.7 && <4.8   default-language:    Haskell2010++test-suite tests+  type: exitcode-stdio-1.0+  main-is: Tests.hs+  default-language:    Haskell2010+  hs-source-dirs:+    tests+  build-depends:+    base,+    cf,+    QuickCheck                 >= 2.4,+    test-framework             >= 0.6,+    test-framework-quickcheck2 >= 0.2,+    test-framework-th          >= 0.2
src/Math/ContinuedFraction.hs view
@@ -1,216 +1,333 @@-module Math.ContinuedFraction-  (-    CF,-    convergents,-    digits,-    showCF,-    sqrt2,-    exp1-  ) where+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeFamilies #-}+module Math.ContinuedFraction (+  CF,+  cfString,+  cfcf+) where +import Data.Maybe (catMaybes, mapMaybe) import Data.Ratio -newtype CF = CF [Integer]+import Math.ContinuedFraction.Interval --- | Produce a list of rational approximations to a number-convergents :: CF -> [Rational]-convergents (CF cf) = go 0 1 1 0 cf-  where go p q p' q' (a:as) = (newp % newq) : go p' q' newp newq as-          where newp = a * p' + p-                newq = a * q' + q-        go _ _ _ _ [] = []+newtype CF' a = CF [a]+type CF = CF' Integer --- | Produce a list of digits in a given base-digits :: Integer -> CF -> [Integer]-digits base (CF cf) = go 0 1 1 0 cf-  where go 0 _ 0 _ _        = []-        go _ _ p' q' []     = go p' q' p' q' [0]-        go p q p' q' (a:as) = case digit p q p' q' of-                                Just d -> d : go (base * (p - d * q)) q (base * (p' - d * q')) q' (a:as)-                                Nothing -> go p' q' (a * p' + p) (a * q' + q) as-        digit p q p' q' = if q' /= 0 && q /= 0 && p `quot` q == p' `quot` q' then-                            Just $ p `quot` q-                          else-                            Nothing+type Hom a = (a, a,+              a, a) --- | Produce a decimal representation of a number-showCF :: CF -> String-showCF cf | cf < 0 = "-" ++ show (-cf)-showCF (CF [i])   = show i-showCF (CF (i:r)) = show i ++ "." ++ decimalDigits-  where decimalDigits = concatMap show $ tail $ digits 10 (CF (0:r))+type Bihom a = (a, a, a, a,+                a, a, a, a) --- Should make this cleverer-instance Show CF where-  show = take 15 . showCF+class (Fractional (FractionField a)) => HasFractionField a where+  type FractionField a :: *+  insert :: a -> FractionField a+  extract :: FractionField a -> (a, a) -safeHead :: [a] -> Maybe a-safeHead (x:_) = Just x-safeHead [] = Nothing+instance HasFractionField Integer where+  type FractionField Integer = Rational+  insert = fromInteger+  extract r = (numerator r, denominator r) -safeRest :: [a] -> [a]-safeRest (_:xs) = xs-safeRest [] = []+instance HasFractionField Rational where+  type FractionField Rational = Rational+  insert = id+  extract r = (numerator r % 1, denominator r % 1) --- The coefficients of the homographic function (a + bx) / (c+dx)-type Hom = (Integer, Integer,-            Integer, Integer)+instance HasFractionField CF where+  type FractionField CF = CF+  insert = id+  extract r = (r, 1) --- Possibly output a term and return the simplified hom-emit :: Hom -> Maybe (Hom, Integer)-emit (a, b,-      c, d) = if c /= 0 && d /= 0 && r == s then-                Just ((c,       d,-                       a - c*r, b-d*r), r)-              else-                Nothing-  where r = a `quot` c-        s = b `quot` d+homEmit :: Num a => Hom a -> a -> Hom a+homEmit (n0, n1,+         d0, d1) x = (d0,        d1,+                      n0 - d0*x, n1 - d1*x) --- Absorb the next term-ingest :: Hom -> Maybe Integer -> Hom-ingest (a, b,-        c, d) (Just p) = (b, a+b*p,-                          d, c+d*p)-ingest (_a, b,-        _c, d) Nothing  = (b, b,-                           d, d)+homAbsorb :: Num a => Hom a -> a -> Hom a+homAbsorb (n0, n1,+           d0, d1) x = (n0*x + n1, n0,+                        d0*x + d1, d0) --- Apply a hom to a continued fraction-hom' :: Hom -> [Integer] -> [Integer]-hom' (0, 0,-      _, _) _ = [0]-hom' (_, _,-      0, 0) _ = []-hom' h x = case emit h of-           Just (next, d) -> d : hom' next x-           Nothing -> hom' (ingest h (safeHead x)) (safeRest x)+det :: Num a => Hom a -> a+det (n0, n1,+     d0, d1) = n0 * d1 - n1 * d0 -hom :: Hom -> CF -> CF-hom h (CF x) = CF $ hom' h x+homEval :: (Num a, HasFractionField a, Eq (FractionField a)) => Hom a -> Extended (FractionField a) -> Extended (FractionField a)+homEval (n0, n1,+         d0, d1) (Finite q) | denom /= 0 = Finite $ num / denom+                            | num == 0 = error "0/0 in homQ"+                            | otherwise = Infinity+  where num   = insert n0 * q + insert n1+        denom = insert d0 * q + insert d1+homEval (n0, _n1,+         d0, _d1) Infinity = Finite $ insert n0 / insert d0 --- The coefficients of the bihomographic function (a + bx + cy + dxy) / (e + fx + gy + hxy)-type Bihom = (Integer, Integer, Integer, Integer,-              Integer, Integer, Integer, Integer)+constantFor :: (Eq a, Num a, HasFractionField a) => Hom a -> Extended (FractionField a)+constantFor (_, _,+             0, 0) = Infinity+constantFor (0, 0,+             0, _) = Finite 0+constantFor (0, 0,+             _, 0) = Finite 0+constantFor (a, 0,+             b, 0) = Finite (insert a / insert b)+constantFor (_, a,+             _, b) = Finite (insert a / insert b) --- Possibly output a term and return the simplified bihom-biemit :: Bihom -> Maybe (Bihom, Integer)-biemit (a, b, c, d,-        e, f, g, h) = if e /= 0 && f /= 0 && g /= 0 && h /= 0 && ratiosAgree then-                      Just ((e,     f,     g,     h ,-                             a-e*r, b-f*r, c-g*r, d-h*r), r)+boundHom :: (Ord a, Num a, HasFractionField a, Eq (FractionField a)) => Hom a -> Interval (FractionField a) -> Interval (FractionField a)+boundHom h (Interval i s) | det h > 0 = Interval i' s'+                          | det h < 0 = Interval s' i'+                          | otherwise = Interval c c+  where i' = homEval h i+        s' = homEval h s+        c = constantFor h++primitiveBound :: forall a. (Ord a, Num a, HasFractionField a) => a -> Interval (FractionField a)+primitiveBound n | abs n < 1 = Interval (Finite $ insert bot) (Finite $ insert top)+  where bot = (-2) :: a+        top = 2 :: a+primitiveBound n = Interval (Finite $ an - 0.5) (Finite $ 0.5 - an)+  where an = insert $ abs n++-- TODO: just take the rational answer from the hom+nthPrimitiveBounds :: (Ord a, Num a, HasFractionField a, Eq (FractionField a)) =>+                       CF' a -> [Interval (FractionField a)]+nthPrimitiveBounds (CF cf) = zipWith boundHom homs (map primitiveBound cf) ++ repeat (Interval ev ev)+  where homs = scanl homAbsorb (1,0,0,1) cf+        ev = evaluate (CF cf)++evaluate :: (HasFractionField a, Eq (FractionField a)) => CF' a -> Extended (FractionField a)+evaluate (CF []) = Infinity+evaluate (CF [c]) = Finite $ insert c+evaluate (CF (c:cs)) = case next of+                        (Finite 0) -> Infinity+                        Infinity   -> Finite $ insert c+                        (Finite r) -> Finite $ insert c + recip r+  where next = evaluate (CF cs)++valueToCF :: RealFrac a => a -> CF+valueToCF r = if rest == 0 then+                CF [d]+              else+                let (CF ds)  = valueToCF (recip rest) in CF (d:ds)+  where (d, rest) = properFraction r++intervalThin :: (RealFrac a) => Interval a -> Bool+intervalThin (Interval Infinity    Infinity)  = False+intervalThin (Interval Infinity   (Finite _)) = False+intervalThin (Interval (Finite _)  Infinity)  = False+intervalThin (Interval (Finite i) (Finite s)) = abs z > 3 || abs (zi - zs) < 2+  where zi = round i+        zs = round s+        z  = if abs zs < abs zi then zs else zi++euclideanPart :: (RealFrac a, Integral b) => Interval a -> Maybe b+euclideanPart (Interval Infinity    Infinity)  = undefined+euclideanPart (Interval Infinity   (Finite b)) = Just $ floor b+euclideanPart (Interval (Finite a)  Infinity)  = Just $ ceiling a+euclideanPart i@(Interval (Finite a) (Finite b))+  | 0 `elementOf` i && not subsetZero = Nothing+  | zi /= 0 && zs /= 0 = Just z+  | subsetZero = Just 0+  | otherwise = Nothing+    where zi = round a+          zs = round b+          z  = if abs zs < abs zi then zs else zi+          subsetZero = i `subset` Interval (Finite (-2)) (Finite 2)++existsEmittable :: RealFrac a => Interval a -> Maybe Integer+existsEmittable i = if intervalThin i then+                      euclideanPart i                     else                       Nothing-  where r = a `quot` e-        ratiosAgree = r == b `quot` f && r == c `quot` g && r == d `quot` h --- Absorb a term from x-ingestX :: Bihom -> Maybe Integer -> Bihom-ingestX (a, b, c, d,-         e, f, g, h) (Just p)  = (b, a+b*p, d, c+d*p,-                                  f, e+f*p, h, g+h*p)-ingestX (_a, b, _c, d,-         _e, f, _g, h) Nothing = (b, b, d, d,-                                  f, f, h, h)--- Absorb a term from y-ingestY :: Bihom -> Maybe Integer -> Bihom-ingestY (a, b, c, d,-         e, f, g, h) (Just q)  = (c, d, a+c*q, b+d*q,-                                  g, h, e+g*q, f+h*q)-ingestY (_a, _b, c, d,-         _e, _f, g, h) Nothing = (c, d, c, d,-                                  g, h, g, h)---- Decide which of x and y to pull a term from-shouldIngestX :: Bihom -> Bool-shouldIngestX (_, _, _, _,-               0, 0, _, _) = False-shouldIngestX (_, _, _, _,-               0, _, 0, _) = True-shouldIngestX (a, b, c, _,-               e, f, g, _) = abs (g*e*b - g*a*f) > abs (f*e*c - g*a*f)+hom :: (Ord a, Num a, HasFractionField a, RealFrac (FractionField a)) => Hom a -> CF' a -> CF+hom (_n0, _n1,+     0,   0) _  = CF []+hom (_n0, _n1,+     0,   _d1) (CF []) = CF []+hom (n0, _n1,+     d0, _d1) (CF []) = valueToCF (insert n0 / insert d0)+hom h (CF (x:xs)) = case existsEmittable $ boundHom h (primitiveBound x) of+                     Just n ->  CF $ n : rest+                       where (CF rest) = hom (homEmit h (fromInteger n)) (CF (x:xs))+                     Nothing -> hom (homAbsorb h x) (CF xs) --- Apply a bihom to two continued fractions-bihom' :: Bihom -> [Integer] -> [Integer] -> [Integer]-bihom' (_, _, _, _,-        0, 0, 0, 0) _ _ = []-bihom' (0, 0, 0, 0,-        _, _, _, _) _ _ = [0]-bihom' bh x y = case biemit bh of-                Just (next, d) -> d : bihom' next x y-                Nothing -> if shouldIngestX bh then-                             bihom' (ingestX bh (safeHead x)) (safeRest x) y-                           else-                             bihom' (ingestY bh (safeHead y)) x (safeRest y)+bihomEmit :: Num a => Bihom a -> a -> Bihom a+bihomEmit (n0, n1, n2, n3,+           d0, d1, d2, d3) x = (d0,        d1,        d2,        d3,+                                n0 - d0*x, n1 - d1*x, n2 - d2*x, n3 - d3*x) -bihom :: Bihom -> CF -> CF -> CF-bihom bh (CF x) (CF y) = CF $ bihom' bh x y+bihomAbsorbX :: Num a => Bihom a -> a -> Bihom a+bihomAbsorbX (n0, n1, n2, n3,+              d0, d1, d2, d3) x = (n0*x + n1, n0, n2*x + n3, n2,+                                   d0*x + d1, d0, d2*x + d3, d2) -sqrt2 :: CF-sqrt2 = CF $ 1 : repeat 2+bihomAbsorbY :: Num a => Bihom a -> a -> Bihom a+bihomAbsorbY (n0, n1, n2, n3,+              d0, d1, d2, d3) y = (n0*y + n2, n1*y + n3, n0, n1,+                                   d0*y + d2, d1*y + d3, d0, d1) -exp1 :: CF-exp1 = CF (2 : concatMap triple [1..])-  where triple n = [1, 2 * n, 1]+bihomSubstituteX :: (Num a, HasFractionField a) => Bihom a -> Extended (FractionField a) -> Hom a+bihomSubstituteX (n0, n1, n2, n3,+                  d0, d1, d2, d3) (Finite x) = (n0*num + n1*den, n2*num + n3*den,+                                                d0*num + d1*den, d2*num + d3*den)+  where (num, den) = extract x+bihomSubstituteX (n0, _n1, n2, _n3,+                  d0, _d1, d2, _d3) Infinity = (n0, n2,+                                                d0, d2) -instance Eq CF where-  x == y = compare x y == EQ+bihomSubstituteY :: (Num a, HasFractionField a) => Bihom a -> Extended (FractionField a) -> Hom a+bihomSubstituteY (n0, n1, n2, n3,+                  d0, d1, d2, d3) (Finite y) = (n0*num + n2*den, n1*num + n3*den,+                                                d0*num + d2*den, d1*num + d3*den)+  where (num, den) = extract y+bihomSubstituteY (n0, n1, _n2, _n3,+                  d0, d1, _d2, _d3) Infinity = (n0, n1,+                                                d0, d1) -instance Ord CF where-  -- As [..., n, 1] represents the same number as [..., n+1]-  compare (CF [x]) (CF [y, 1]) = compare x (y+1)-  compare (CF [x, 1]) (CF [y]) = compare (x+1) y-  compare (CF [x]) (CF [y]) = compare x y+boundBihom :: (Ord a, Num a, HasFractionField a, Eq (FractionField a), Ord (FractionField a)) =>+              Bihom a -> Interval (FractionField a) -> Interval (FractionField a) -> Interval (FractionField a)+boundBihom bh x@(Interval ix sx) y@(Interval iy sy) = r1 `mergeInterval` r2 `mergeInterval` r3 `mergeInterval` r4+  where r1 = boundHom (bihomSubstituteX bh ix) y+        r2 = boundHom (bihomSubstituteY bh iy) x+        r3 = boundHom (bihomSubstituteX bh sx) y+        r4 = boundHom (bihomSubstituteY bh sy) x -  compare (CF (x:_)) (CF [y]) = if x < y then LT else GT-  compare (CF [x]) (CF (y:_)) = if x > y then GT else LT+select :: (Ord a, Num a, HasFractionField a, Eq (FractionField a), Ord (FractionField a)) =>+          Bihom a -> Interval (FractionField a) -> Interval (FractionField a) -> Bool+select bh x@(Interval ix sx) y@(Interval iy sy) = intX `smallerThan` intY+  where intX = if r1 `smallerThan` r2 then r2 else r1+        intY = if r3 `smallerThan` r4 then r3 else r4+        r1 = boundHom (bihomSubstituteX bh ix) y+        r2 = boundHom (bihomSubstituteX bh sx) y+        r3 = boundHom (bihomSubstituteY bh iy) x+        r4 = boundHom (bihomSubstituteY bh sy) x -  compare (CF (x:xs)) (CF (y:ys)) = case compare x y of-                                     EQ -> opposite $ compare (CF xs) (CF ys)-                                     o  -> o-    where opposite LT = GT-          opposite EQ = EQ-          opposite GT = LT+bihom :: (Ord a, Num a, HasFractionField a, RealFrac (FractionField a))+         => Bihom a -> CF' a -> CF' a -> CF+bihom bh (CF []) y = hom (bihomSubstituteX bh Infinity) y+bihom bh x (CF []) = hom (bihomSubstituteY bh Infinity) x+bihom bh (CF (x:xs)) (CF (y:ys)) = case existsEmittable $ boundBihom bh (primitiveBound x) (primitiveBound y) of+                   Just n -> CF $ n : rest+                     where (CF rest) = bihom (bihomEmit bh (fromInteger n)) (CF (x:xs)) (CF (y:ys))+                   Nothing -> if select bh (primitiveBound x) (primitiveBound y) then+                                let bh' = bihomAbsorbX bh x in bihom bh' (CF xs) (CF (y:ys))+                              else+                                let bh' = bihomAbsorbY bh y in bihom bh' (CF (x:xs)) (CF ys)  instance Num CF where   (+) = bihom (0, 1, 1, 0,-               1, 0, 0, 0)-  (*) = bihom (0, 0, 0, 1,-               1, 0, 0, 0)-  (-) = bihom (0, 1, -1, 0,-               1, 0,  0, 0)+               0, 0, 0, 1)+  (-) = bihom (0, -1, 1, 0,+               0,  0, 0, 1)+  (*) = bihom (1, 0, 0, 0,+               0, 0, 0, 1) -  fromInteger i = CF [i]-  abs x = if x > 0 then-             x-          else-            -x-  signum x | x < 0  = -1-           | x == 0 = 0-           | x > 0 = 1+  fromInteger n = CF [n] -instance Enum CF where-  toEnum = fromInteger . fromIntegral-  fromEnum = floor+  signum x = case 0 `compare` x of+              EQ -> 0+              LT -> 1+              GT -> -1 +  abs x | x < 0     = -x+        | otherwise = x+ instance Fractional CF where-  (/) = bihom (0, 1, 0, 0,-               0, 0, 1, 0)+  (/) = bihom (0, 0, 1, 0,+               0, 1, 0, 0) -  recip (CF [1]) = CF [1]-  recip (CF (0:xs)) = CF xs-  recip (CF xs) = CF (0:xs)+  fromRational = valueToCF -  fromRational r = fromInteger n / fromInteger d-    where n = numerator r-          d = denominator r+base :: Integer+base = 10 +rationalDigits :: Rational -> [Integer]+rationalDigits 0 = []+rationalDigits r = let d = num `quot` den in+                   d : rationalDigits (fromInteger base * (r - fromInteger d))+  where num = numerator r+        den = denominator r++digits :: CF -> [Integer]+digits = go (1, 0, 0, 1)+  where go (0, 0, _, _) _ = []+        go (p, _, q, _) (CF []) = rationalDigits (p % q)+        go h (CF (c:cs)) = case intervalDigit $ boundHom h (primitiveBound c) of+                            Nothing -> let h' = homAbsorb h c in go h' (CF cs)+                            Just d  -> d : go (homEmitDigit h d) (CF (c:cs))+        homEmitDigit (n0, n1,+                      d0, d1) d = (base * (n0 - d0*d), base * (n1 - d1*d),+                                   d0,                 d1)++cfString :: CF -> String+cfString (CF []) = "Infinity"+cfString cf | cf < 0 = '-' : cfString (-cf)+cfString cf = case digits cf of+               []     -> "0"+               [i]    -> show i+               (i:is) -> show i ++ "." ++ concatMap show is++instance Show CF where+  show = take 50 . cfString++instance Eq CF where+  a == b = a `compare` b == EQ++instance Ord CF where+  a `compare` b = head $ catMaybes $ zipWith comparePosition (nthPrimitiveBounds a) (nthPrimitiveBounds b)+ instance Real CF where-  -- Just take a pretty good rational approximation-  toRational cf = last $ take 20 (convergents cf)+  toRational = error "CF: toRational"  instance RealFrac CF where-  properFraction (CF [i]) = (fromIntegral i, 0)-  properFraction cf | cf < 0 = case properFraction (-cf) of-                                (b, a) -> (-b, -a)-  properFraction (CF (i:r)) = (fromIntegral i, CF r)+  properFraction cf = head $ mapMaybe checkValid $ nthPrimitiveBounds cf+    where checkValid (Interval (Finite a) (Finite b)) = if a <= b && truncate a == truncate b then+                                                          Just (truncate a, cf - fromInteger (truncate a))+                                                        else+                                                          Nothing+          checkValid _ = Nothing++cfcf :: CF' CF -> CF+cfcf = hom (1, 0, 0, 1)++instance Floating CF where+  exp r = cfcf (CF $ 1 : concatMap go [0..])+    where go n = [fromInteger (4*n+1) / r,+                  -2,+                  -fromInteger (4*n+3) / r,+                  2]++  -- TODO: restrict range+  log r = cfcf (CF $ 0 : concatMap go [0..])+    where go n = [fromInteger (2*n+1) / (r-1),+                  fromRational $ 2 % (n+1)]++  tan r = cfcf (CF $ 0 : concatMap go [0..])+    where go n = [fromInteger (4*n+1) / r,+                  -fromInteger (4*n+3) / r]++  sin r = bihom (0,2,0,0,+                 1,0,0,1) tanhalf tanhalf+    where tanhalf = tan (r / 2)++  cos r = bihom (-1,0,0,1,+                  1,0,0,1) tanhalf tanhalf+    where tanhalf = tan (r / 2)++  sinh r = bihom (1,0,0,-1,+                  0,1,1, 0) expr expr+    where expr = exp r++  cosh r = bihom (1,0,0,1,+                  0,1,1,0) expr expr+    where expr = exp r++  tanh r = bihom (1,0,0,-1,+                  1,0,0, 1) expr expr+    where expr = exp r
+ src/Math/ContinuedFraction/Interval.hs view
@@ -0,0 +1,163 @@+{-# LANGUAGE FlexibleInstances #-}+module Math.ContinuedFraction.Interval where++import Data.Ratio+import Numeric++data Extended a = Finite a | Infinity deriving (Eq)++data Interval a = Interval (Extended a) (Extended a) deriving (Eq)++instance Show (Interval Rational) where+  show (Interval a b) = "(" ++ showE a ++ ", " ++ showE b ++ ")"+    where showE Infinity = "Infinity"+          showE (Finite r) = show (fromRat r)++instance Num a => Num (Extended a) where+  Finite a + Finite b = Finite (a + b)+  Infinity + Finite _ = Infinity+  Finite _ + Infinity = Infinity+  Infinity + Infinity = error "Infinity + Infinity"++  Finite a * Finite b = Finite (a * b)+  Infinity * Finite a = Infinity+  -- Infinity * Finite a | a == 0 = error "Infinity * 0"+  --                     | otherwise = Infinity+  Finite a * i = i * Finite a+  Infinity * Infinity = undefined "Infinity * Infinity"++  negate (Finite r) = Finite (-r)+  negate Infinity = Infinity++  signum (Finite r) = Finite $ signum r+  signum Infinity = error "signum Infinity"++  abs (Finite r) = Finite $ abs r+  abs Infinity = Infinity++  fromInteger = Finite . fromInteger++instance (Show a) => Show (Extended a) where+  show (Finite r) = show r+  show Infinity = "Infinity"++smallerThan :: (Num a, Ord a) => Interval a -> Interval a -> Bool+Interval _ _ `smallerThan` Interval Infinity Infinity = False -- TODO CHECK+Interval Infinity Infinity `smallerThan` Interval _ _ = True+Interval (Finite a) Infinity `smallerThan` Interval (Finite b) Infinity = a >= b+Interval (Finite a) Infinity `smallerThan` Interval Infinity (Finite b) = a >= -b+Interval Infinity (Finite a) `smallerThan` Interval (Finite b) Infinity = a <= -b+Interval Infinity (Finite a) `smallerThan` Interval Infinity (Finite b) = a <= b+Interval (Finite i1) (Finite s1) `smallerThan` Interval Infinity (Finite _) = i1 <= s1+Interval (Finite i1) (Finite s1) `smallerThan` Interval (Finite _) Infinity = i1 <= s1+Interval Infinity (Finite _) `smallerThan` Interval (Finite i2) (Finite s2) = i2 > s2+Interval (Finite _) Infinity `smallerThan` Interval (Finite i2) (Finite s2) = i2 > s2+-- TODO: cache some of these comparisons+Interval (Finite i1) (Finite s1) `smallerThan` Interval (Finite i2) (Finite s2)+  =    (i1 <= s1 && i2 <= s2 && s1 - i1 <= s2 - i2)+    || (i1 >  s1 && i2 >  s2 && i1 - s1 >= i2 - s2)+    || (i1 <= s1 && i2 >  s2)++epsilon :: Rational+epsilon = 1 % 10^10++comparePosition :: Interval Rational -> Interval Rational -> Maybe Ordering+Interval (Finite i1) (Finite s1) `comparePosition` Interval (Finite i2) (Finite s2)+  | i1 > s1 = Nothing+  | i2 > s2 = Nothing+  | s1 < i2 = Just LT+  | s2 < i1 = Just GT+  | (s1 - i1) < epsilon && (s2 - i2) < epsilon = Just EQ+_ `comparePosition` _ = Nothing++intervalDigit :: (RealFrac a) => Interval a -> Maybe Integer+intervalDigit (Interval (Finite i) (Finite s)) = if i <= s && floor i == floor s && floor i >= 0 then+                                                   Just $ floor i+                                                 else+                                                   Nothing+intervalDigit _ = Nothing++subset :: Ord a => Interval a -> Interval a -> Bool+Interval _ _ `subset` Interval Infinity Infinity = True+Interval Infinity Infinity `subset` Interval _ _ = False+Interval Infinity (Finite s1) `subset` Interval Infinity (Finite s2) = s1 <= s2+Interval (Finite i1) Infinity `subset` Interval (Finite i2) Infinity = i1 >= i2+Interval Infinity (Finite _) `subset` Interval (Finite _) Infinity = False+Interval (Finite _) Infinity `subset` Interval Infinity (Finite _) = False+Interval (Finite i1) (Finite s1) `subset` Interval Infinity (Finite s2)+  | i1 <= s1 && s1 <= s2 = True+  | otherwise            = False+Interval (Finite i1) (Finite s1) `subset` Interval (Finite i2) Infinity+  | i1 <= s1 && i2 <= i1 = True+  | otherwise            = False+Interval Infinity (Finite s1) `subset` Interval (Finite i2) (Finite s2)+  | i2 > s2 && s1 <= s2 = True+  | otherwise            = False+Interval (Finite i1) Infinity `subset` Interval (Finite i2) (Finite s2)+  | i2 > s2 && i2 <= i1 = True+  | otherwise            = False+Interval (Finite i1) (Finite s1) `subset` Interval (Finite i2) (Finite s2)+  | i1 <= s1 && i2 <= s2 &&+    i2 <= i1 && s1 <= s2     = True+  | s1 <  i1 && s2 <  i2 &&+    i2 <= i1 && s1 <= s2     = True+  | i1 <= s1 && s2 <  i2 &&+    i2 <= i1 && i2 <= s1     = True+  | i1 <= s1 && s2 <  i2 &&+    i1 <= s2 && s1 <= s2     = True+  | otherwise                = False++elementOf :: (Ord a) => Extended a -> Interval a -> Bool+Infinity `elementOf` (Interval Infinity Infinity) = True+(Finite _) `elementOf` (Interval Infinity Infinity) = True+Infinity `elementOf` (Interval (Finite _) Infinity) = True+(Finite x) `elementOf` (Interval (Finite a) Infinity) = x >= a+Infinity `elementOf` (Interval Infinity (Finite _)) = True+(Finite x) `elementOf` (Interval Infinity (Finite b)) = x <= b+Infinity `elementOf` (Interval (Finite i) (Finite s)) = i > s+(Finite x) `elementOf` (Interval (Finite i) (Finite s))+  | i <= s = i <= x && x <= s+  | i >  s = i <= x || x <= s+  | otherwise = error "The impossible happened in elementOf"++-- Here we interpret Interval Infinity Infinity as the whole real line+mergeInterval :: (Ord a) => Interval a -> Interval a -> Interval a+mergeInterval (Interval Infinity Infinity) (Interval Infinity Infinity)+  = Interval Infinity Infinity+mergeInterval (Interval (Finite i) Infinity) (Interval Infinity Infinity)+  = Interval Infinity Infinity+mergeInterval (Interval Infinity (Finite s)) (Interval Infinity Infinity)+  = Interval Infinity Infinity+mergeInterval (Interval (Finite i) (Finite s)) (Interval Infinity Infinity)+  = Interval Infinity Infinity+mergeInterval (Interval Infinity (Finite s)) (Interval (Finite i) Infinity)+  | s >= i    = Interval Infinity Infinity+  | otherwise = Interval (Finite i) (Finite s)+mergeInterval (Interval Infinity (Finite s1)) (Interval Infinity (Finite s2))+  = Interval Infinity (Finite $ max s1 s2)+mergeInterval (Interval (Finite i1) Infinity) (Interval (Finite i2) Infinity)+  = Interval Infinity (Finite $ min i1 i2)+mergeInterval (Interval (Finite i1) (Finite s1)) (Interval (Finite i2) Infinity)+  | i1 <= s1 = Interval (Finite $ min i1 i2) Infinity+  | i1 >  s1 && i1 <= i2 = Interval (Finite i1) (Finite s1)+  | i1 >  s1 && i2 <= s1 = Interval Infinity Infinity+  | i1 >  s1 && i2 >  s1 = Interval (Finite i2) (Finite s1)+mergeInterval (Interval (Finite i1) (Finite s1)) (Interval Infinity (Finite s2))+  | i1 <= s1 = Interval Infinity (Finite $ max s1 s2)+  | i1 >  s1 && s2 <= s1 = Interval (Finite i1) (Finite s1)+  | i1 >  s1 && i1 <= s2 = Interval Infinity Infinity+  | i1 >  s1 && i1 >  s2 = Interval (Finite i1) (Finite s2)+mergeInterval int1@(Interval (Finite i1) (Finite s1)) int2@(Interval (Finite i2) (Finite s2))+  | i1 <= s1 && i2 <= s2 = Interval (Finite $ min i1 i2) (Finite $ max s1 s2)+  | i1 >  s1 && i2 >  s2 && (i1 <= s2 || i2 <= s1) = Interval Infinity Infinity+  | i1 >  s1 && i2 >  s2 = Interval (Finite $ min i1 i2) (Finite $ max s1 s2)+  | i1 >  s1 && i2 <= s2 = doTricky int2 int1+  | i1 <= s1 && i2 >  s2 = doTricky int1 int2+  | otherwise = error "The impossible happened in mergeInterval"+  where doTricky int1@(Interval (Finite i1) (Finite s1)) int2@(Interval (Finite i2) (Finite s2))+          | int1 `subset` int2         = int2+          | i2 <= s1 && i1 <= s2       = Interval Infinity Infinity+          | s1 < i2  = Interval (Finite i2) (Finite s1)+          | s2 < i1  = Interval (Finite i1) (Finite s2)+          | otherwise = error "The impossible happened in mergeInterval"+mergeInterval int1 int2 = mergeInterval int2 int1
+ src/Math/ContinuedFraction/Simple.hs view
@@ -0,0 +1,201 @@+module Math.ContinuedFraction.Simple+  (+    CF,+    digits,+    showCF,+    sqrt2,+    exp1+  ) where++import Data.Ratio++newtype CF = CF [Integer]++-- The coefficients of the homographic function (ax + b) / (cx + d)+type Hom = (Integer, Integer,+            Integer, Integer)++-- Possibly output a term+homEmittable :: Hom -> Maybe Integer+homEmittable (a, b,+              c, d) = if c /= 0 && d /= 0 && r == s then+                        Just r+                      else+                        Nothing+  where r = a `quot` c+        s = b `quot` d++homEmit :: Hom -> Integer -> Hom+homEmit (n0, n1,+         d0, d1) x = (d0,        d1,+                      n0 - d0*x, n1 - d1*x)++homAbsorb :: Hom -> Integer -> Hom+homAbsorb (n0, n1,+           d0, d1) x = (n0*x + n1, n0,+                        d0*x + d1, d0)++-- Apply a hom to a continued fraction+hom :: Hom -> CF -> CF+hom (0, 0,+     _, _) _ = CF [0]+hom (_, _,+     0, 0) _ = CF []+hom (n0, _,+     d0, _) (CF []) = fromRational (n0 % d0)+hom h (CF (x:xs)) = case homEmittable h of+                     Just d -> let (CF rest) = hom (homEmit h d) (CF (x:xs)) in CF (d : rest)+                     Nothing -> hom (homAbsorb h x) (CF xs)++-- The coefficients of the bihomographic function (axy + by + cx + d) / (exy + fy + gx + h)+type Bihom = (Integer, Integer, Integer, Integer,+              Integer, Integer, Integer, Integer)++bihomEmittable :: Bihom -> Maybe Integer+bihomEmittable (a, b, c, d,+                e, f, g, h) = if e /= 0 && f /= 0 && g /= 0 && h /= 0 && ratiosAgree then+                                Just r+                              else+                                Nothing+  where r = a `quot` e+        ratiosAgree = r == b `quot` f && r == c `quot` g && r == d `quot` h++bihomEmit :: Bihom -> Integer -> Bihom+bihomEmit (n0, n1, n2, n3,+           d0, d1, d2, d3) x = (d0,        d1,        d2,        d3,+                                n0 - d0*x, n1 - d1*x, n2 - d2*x, n3 - d3*x)++bihomAbsorbX :: Bihom -> Integer -> Bihom+bihomAbsorbX (n0, n1, n2, n3,+              d0, d1, d2, d3) x = (n0*x + n1, n0, n2*x + n3, n2,+                                   d0*x + d1, d0, d2*x + d3, d2)++bihomAbsorbY :: Bihom -> Integer -> Bihom+bihomAbsorbY (n0, n1, n2, n3,+              d0, d1, d2, d3) y = (n0*y + n2, n1*y + n3, n0, n1,+                                   d0*y + d2, d1*y + d3, d0, d1)++-- Decide which of x and y to pull a term from+shouldIngestX :: Bihom -> Bool+shouldIngestX (_, _, _, _,+               _, 0, _, 0) = True+shouldIngestX (_, _, _, _,+               _, _, 0, 0) = False+shouldIngestX (_a, b, c, d,+               _e, f, g, h) = abs (g*h*b - g*d*f) < abs (f*h*c - g*d*f)++-- Apply a bihom to two continued fractions+bihom :: Bihom -> CF -> CF -> CF+bihom (_, _, _, _,+       0, 0, 0, 0) _ _ = CF []+bihom (0, 0, 0, 0,+       _, _, _, _) _ _ = CF [0]+bihom (n0, _n1, n2, _n3,+       d0, _d1, d2, _d3) (CF []) y = hom (n0, n2,+                                          d0, d2) y+bihom (n0, n1, _n2, _n3,+       d0, d1, _d2, _d3) x (CF []) = hom (n0, n1,+                                          d0, d1) x+bihom bh (CF (x:xs)) (CF (y:ys)) = case bihomEmittable bh of+                                    Just d -> CF $ d : rest+                                      where (CF rest) = bihom (bihomEmit bh d) (CF (x:xs)) (CF (y:ys))+                                    Nothing -> if shouldIngestX bh then+                                                 bihom (bihomAbsorbX bh x) (CF xs) (CF (y:ys))+                                               else+                                                 bihom (bihomAbsorbY bh y) (CF (x:xs)) (CF ys)++sqrt2 :: CF+sqrt2 = CF $ 1 : repeat 2++exp1 :: CF+exp1 = CF (2 : concatMap triple [1..])+  where triple n = [1, 2 * n, 1]++instance Eq CF where+  x == y = compare x y == EQ++instance Ord CF where+  -- As [..., n, 1] represents the same number as [..., n+1]+  compare (CF [x]) (CF [y, 1]) = compare x (y+1)+  compare (CF [x, 1]) (CF [y]) = compare (x+1) y+  compare (CF [x]) (CF [y]) = compare x y++  compare (CF (x:_)) (CF [y]) = if x < y then LT else GT+  compare (CF [x]) (CF (y:_)) = if x > y then GT else LT++  compare (CF (x:xs)) (CF (y:ys)) = case compare x y of+                                     EQ -> opposite $ compare (CF xs) (CF ys)+                                     o  -> o+    where opposite LT = GT+          opposite EQ = EQ+          opposite GT = LT++instance Num CF where+  (+) = bihom (0, 1, 1, 0,+               0, 0, 0, 1)+  (*) = bihom (1, 0, 0, 0,+               0, 0, 0, 1)+  (-) = bihom (0, -1, 1, 0,+               0,  0, 0, 1)++  fromInteger i = CF [i]+  abs x = if x > 0 then+             x+          else+            -x+  signum x | x < 0  = -1+           | x == 0 = 0+           | x > 0 = 1+++instance Fractional CF where+  (/) = bihom (0, 0, 1, 0,+               0, 1, 0, 0)++  recip (CF [1]) = CF [1]+  recip (CF (0:xs)) = CF xs+  recip (CF xs) = CF (0:xs)++  fromRational r = if rest == 0 then+                CF [d]+              else+                let (CF ds)  = fromRational (recip rest) in CF (d:ds)+    where (d, rest) = properFraction r++instance Real CF where+  toRational _ = undefined++instance RealFrac CF where+  properFraction (CF [i]) = (fromIntegral i, 0)+  properFraction cf | cf < 0 = case properFraction (-cf) of+                                (b, a) -> (-b, -a)+  properFraction (CF (i:r)) = (fromIntegral i, CF r)++rationalDigits :: Rational -> [Integer]+rationalDigits 0 = []+rationalDigits r = let d = num `quot` den in+                   d : rationalDigits (fromInteger 10 * (r - fromInteger d))+  where num = numerator r+        den = denominator r++digits :: CF -> [Integer]+digits = go (1, 0, 0, 1)+  where go (0, 0, _, _) _ = []+        go (p, _, q, _) (CF []) = rationalDigits (p % q)+        go h (CF (c:cs)) = case homEmittable h of+                            Nothing -> let h' = homAbsorb h c in go h' (CF cs)+                            Just d  -> d : go (homEmitDigit h d) (CF (c:cs))+        homEmitDigit (n0, n1,+                      d0, d1) d = (10 * (n0 - d0*d), 10 * (n1 - d1*d),+                                   d0,               d1)++-- | Produce a decimal representation of a number+showCF :: CF -> String+showCF cf | cf < 0 = "-" ++ show (-cf)+showCF (CF [i])   = show i+showCF (CF (i:r)) = show i ++ "." ++ decimalDigits+  where decimalDigits = concatMap show $ tail $ digits (CF (0:r))++-- Should make this cleverer+instance Show CF where+  show = take 15 . showCF
+ tests/Tests.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE TemplateHaskell, FlexibleInstances #-}+module Main where++import Data.Maybe+import Data.Ratio++import Math.ContinuedFraction+import Math.ContinuedFraction.Interval++import Test.QuickCheck+import Test.QuickCheck.Function+import Test.Framework.TH+import Test.Framework.Providers.QuickCheck2++instance Arbitrary (Extended Rational) where+  arbitrary = do+    b <- arbitrary :: Gen Bool+    if b then+      return Infinity+    else do+      n <- choose (-10, 10)+      return $ Finite (n % 1)++instance Arbitrary (Interval Rational) where+  arbitrary = do+    (i, s) <- suchThat arbitrary (\(i,s) -> i /= s) :: Gen (Extended Rational, Extended Rational)+    return $ Interval i s++prop_sensiblePrimitiveBound x = fromInteger x `elementOf` primitiveBound x+  where types = x :: Integer++prop_sensibleMergeInterval a b = a `subset` ab && b `subset` ab+  where types = (a :: Interval Rational, b :: Interval Rational)+        ab = a `mergeInterval` b++finitePrimitiveBounds (CF cf) = zipWith boundHom homs (map primitiveBound cf)+  where homs = scanl homAbsorb (1,0,0,1) cf++prop_primitiveBoundsContain a b = all ((Finite $ a + b) `elementOf`) $ finitePrimitiveBounds (valueToCF a + valueToCF b)+  where types = (a :: Rational, b :: Rational)++prop_sensibleEuclidean i = case existsEmittable i of+                            Just n -> i `subset` primitiveBound n+                            Nothing -> True+  where types = i :: Interval Rational++main :: IO ()+main = $defaultMainGenerator