-- | Class instances for the continued fraction datatype
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}

module Data.Number.Instances where

import Data.Number.Types
import Data.Number.Internal

-- | transform a number applying a function ('Nat' -> 'Nat') to each
-- number preserving the sign.
instance Functor Continued where
  fmap _ E       = E
  fmap f (M x)   = M (fmap f x)
  fmap f (x:|xs) = f x :| fmap f xs 

-- | Basic Foldable instance implemented exactly as a list.
instance Foldable Continued where
  foldr f z E       = z
  foldr f z (M x)   = foldr f z x
  foldr f z (x:|xs) = f x (foldr f z xs)

-- | Same as above.
instance Traversable Continued where
  traverse _ E       = pure E
  traverse f (M x)   = traverse f x
  traverse f (x:|xs) = (:|) <$> f x <*> traverse f xs 

-- | The sign is given by the first number of the fraction.
-- Other number are always considered positive.
instance Num Number where
  (+) = biHom (0, 1,  1, 0, 1, 0, 0, 0)
  (-) = biHom (0, 1, -1, 0, 1, 0, 0, 0)
  (*) = biHom (0, 0,  0, 1, 1, 0, 0, 0)

  abs (M x) = x
  abs x = x

  negate (M x) = x
  negate x = M x

  fromInteger = toNumber . fromIntegral

  signum E     = 0
  signum (M _) = -1
  signum _     = 1

-- | Allows conversion to a rational
instance Real Number where
  toRational = fromNumber

-- | Allows division between 'Number's and conversion from a rational
instance Fractional Number where
  (/) = biHom (0, 1, 0, 0, 0, 0, 1, 0)
  fromRational = toNumber


-- Helpers --

-- | Convert a 'Number' into a 'RealFrac' number
fromNumber :: RealFrac a => Number -> a
fromNumber E         = 0
fromNumber (M x)     = negate (fromNumber x)
fromNumber (x :| E)  = fromIntegral x
fromNumber (x :| xs) = fromIntegral x + 1 / (fromNumber xs)