module Data.Number.Flint.Arb.RealField (
  RF(..)
, RF'(..)
, Special (..)
, fromDouble
, toDouble
) where

import GHC.TypeLits
import Data.Proxy

import GHC.Read 
import qualified Text.Read.Lex as Lex
import Text.ParserCombinators.ReadPrec hiding (prec)

import Data.Ratio

import System.IO.Unsafe
import Control.Monad

import Foreign.C.String
import Foreign.C.Types
import Foreign.ForeignPtr
import Foreign.Ptr ( Ptr, FunPtr )
import Foreign.Storable
import Foreign.Marshal (free)

import Data.Number.Flint.Fmpz
import Data.Number.Flint.Fmpz.Instances
import Data.Number.Flint.Arb
import Data.Number.Flint.Arb.Arf
import Data.Number.Flint.Arb.Mag
import Data.Number.Flint.Arb.Types
import Data.Number.Flint.Arb.Hypgeom
import Data.Number.Flint.Support.D.Interval

newtype RF (n :: Nat) = RF Arb

instance forall n. KnownNat n => Eq (RF n) where
  {-# INLINE (==) #-}
  (==) = liftCmp arb_eq
  {-# INLINE (/=) #-}
  (/=) = liftCmp arb_ne

instance forall n. KnownNat n => Ord (RF n) where
  {-# INLINE (<) #-}
  (<) = liftCmp arb_lt
  {-# INLINE (<=) #-}
  (<=) = liftCmp arb_le
  {-# INLINE (>) #-}
  (>) = liftCmp arb_gt
  {-# INLINE (>=) #-}
  (>=) = liftCmp arb_ge
  {-# INLINE max #-}
  max = lift2 arb_max
  {-# INLINE min #-}
  min = lift2 arb_min
  
instance forall n. KnownNat n => Num (RF n) where
  {-# INLINE (+) #-}
  (+) = lift2 arb_add
  {-# INLINE (-) #-}
  (-) = lift2 arb_sub
  {-# INLINE (*) #-}
  (*) = lift2 arb_mul
  {-# INLINE negate #-}
  negate = lift1 arb_neg
  {-# INLINE abs #-}
  abs = lift1 arb_abs
  {-# INLINE fromInteger #-}
  fromInteger x = unsafePerformIO $ do
    let prec = fromInteger $ natVal (Proxy :: Proxy n)
    result <- newArb
    withArb result $ \result -> do
      withCString (show x) $ \s -> do
        flag <- arb_set_str result s prec
        when (flag /= 0) $
          error $ "Could not create RF " ++ show prec ++ " from " ++ show x
    return (RF result)
  {-# INLINE signum #-}
  signum = lift1 arb_sgn
  
instance forall n. KnownNat n => Fractional (RF n) where
  {-# INLINE (/) #-}
  (/) = lift2 arb_div
  fromRational x = p / q where
    p = fromIntegral (numerator x) :: RF n
    q = fromIntegral (denominator x) :: RF n

instance forall n. KnownNat n => RealFloat (RF n) where
  isNaN = not . liftProp arb_is_finite
  isInfinite = not . liftProp arb_is_finite
  floatRadix _ = 2
  floatDigits _ = fromIntegral $ natVal (Proxy :: Proxy n)
  floatRange _ = (minBound :: Int, maxBound :: Int)
  decodeFloat (RF x) = unsafePerformIO $ do
    man <- newFmpz
    exp <- newFmpz 
    withArb x $ \a -> do
      withFmpz man $ \man -> do
        withFmpz exp $ \exp -> do
          arf_get_fmpz_2exp man exp (arb_midref a)
    return (toInteger man, fromIntegral exp)
  encodeFloat man exp = unsafePerformIO $ do
    let prec = fromInteger $ natVal (Proxy :: Proxy n)
        m = fromIntegral man :: Fmpz
        e = fromIntegral exp :: Fmpz
    res <- newArb
    withArb res $ \res -> do 
      withFmpz m $ \m -> do
        withFmpz e $ \e -> do
          withNewArf $ \ arf -> do
            arf_set_round_fmpz_2exp arf m e prec arf_rnd_near
            arb_set_arf res arf
    return $ RF res
  isDenormalized = error "isDenormalized: not defined"
  isNegativeZero = error "isNegativeZero: not defined"
  isIEEE _ = False
  atan2 = lift2 arb_atan2

instance forall n. KnownNat n => Real (RF n) where
  toRational x =
    case decodeFloat x of
      (m, n) -> if n >= 0 then (m*2^n)%1 else m % (2^(-n))

instance forall n. KnownNat n => RealFrac (RF n) where
  properFraction x
    = case (decodeFloat x) of { (m,n) ->
      if n >= 0 then
          (fromInteger m * 2 ^ n, 0.0)
      else
          case (quotRem m (2^(negate n))) of { (w,r) ->
          (fromInteger w, encodeFloat r n)
          }
      }

instance forall n. KnownNat n => Floating (RF n) where
  pi = liftConstant arb_const_pi
  exp = liftF1 arb_exp
  log = liftF1 arb_log
  sqrt = liftF1 arb_sqrt
  sin = liftF1 arb_sin
  cos = liftF1 arb_cos
  tan = liftF1 arb_tan
  asin = liftF1 arb_asin
  acos = liftF1 arb_acos
  atan = liftF1 arb_atan
  sinh = liftF1 arb_sinh
  cosh = liftF1 arb_cosh
  tanh = liftF1 arb_tanh
  asinh = liftF1 arb_asinh
  acosh = liftF1 arb_acosh
  atanh = liftF1 arb_atanh
  
instance forall n. KnownNat n => Show (RF n) where
  show (RF x) = unsafePerformIO $ do
    let prec = fromInteger $ natVal (Proxy :: Proxy n)
        digits = floor (fromIntegral prec * logBase 10 2)
    (_, cstr) <- withArb x $ \p ->
      arb_get_str p (fromIntegral digits) arb_str_no_radius
    str <- peekCString cstr
    return str

instance forall n. KnownNat n => Read (RF n) where
  readPrec     = readNumber convertFrac
  readListPrec = readListPrecDefault
  readList     = readListDefault

convertFrac :: RealFloat a => Lex.Lexeme -> ReadPrec a
convertFrac (Lex.Ident "NaN")      = return (0 / 0)
convertFrac (Lex.Ident "Infinity") = return (1 / 0)
convertFrac (Lex.Number n) = let resRange = floatRange (undefined :: a)
                           in case Lex.numberToRangedRational resRange n of
                              Nothing -> return (1 / 0)
                              Just rat -> return $ fromRational rat
convertFrac _            = pfail

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

instance forall n. KnownNat n => Special (RF n) where
  gamma = liftF1 arb_gamma
  digamma = liftF1 arb_digamma
  lgamma = liftF1 arb_hypgeom_lgamma
  zeta = liftF1 arb_zeta
  erf = liftF1 arb_hypgeom_erf
  airy (RF x) = unsafePerformIO $ do
    let prec = fromInteger $ natVal (Proxy :: Proxy n)
    ai <- newArb
    ai' <- newArb
    bi <- newArb
    bi' <- newArb
    withArb x $ \x -> 
      withArb ai $ \ai -> 
        withArb ai' $ \ai' ->
          withArb bi $ \bi ->
            withArb bi' $ \bi' ->
              arb_hypgeom_airy ai ai' bi bi' x prec
    return $ (RF ai, RF ai', RF bi, RF bi')
  airyZeros k = unsafePerformIO $ do
    let prec = fromInteger $ natVal (Proxy :: Proxy n)
    ai <- newArb
    ai' <- newArb
    bi <- newArb
    bi' <- newArb
    withFmpz k $ \k -> 
      withArb ai $ \ai -> 
        withArb ai' $ \ai' ->
          withArb bi $ \bi ->
            withArb bi' $ \bi' ->
              arb_hypgeom_airy_zero ai ai' bi bi' k prec
    return $ (RF ai, RF ai', RF bi, RF bi')
  besselJ = lift2 arb_hypgeom_bessel_j
  besselY = lift2 arb_hypgeom_bessel_y
  besselI = lift2 arb_hypgeom_bessel_i
  besselK = lift2 arb_hypgeom_bessel_k
  modj = undefined
  modjq = undefined
  modeta = undefined
  modetaq = undefined
  modlambda = undefined
  modlambdaq = undefined
  ellipp = undefined
  ellipzeta = undefined
  ellipsigma = undefined
  barnesg = undefined
  agm = undefined
  fresnels = undefined
  fresnelc = undefined
  
class RF' a where
  euler :: a
  glaisher :: a
  catalan :: a
  khinchin :: a
  polylog :: a -> a -> a
  midPoint :: a -> a
  
instance forall n. KnownNat n => RF' (RF n) where
  euler = liftConstant arb_const_euler
  glaisher = liftConstant arb_const_glaisher
  catalan = liftConstant arb_const_catalan
  khinchin = liftConstant arb_const_khinchin
  polylog = lift2 arb_polylog
  midPoint = lift1 arb_get_mid_arb

fromDouble ::  forall n. KnownNat n => Double -> RF n
fromDouble x = unsafePerformIO $ do
  res <- newArb
  withArb res $ \res -> arb_set_d res (realToFrac x)
  return $ RF res
  
toDouble :: forall n. KnownNat n => RF n -> Double
toDouble x = fromRational $ toRational x
    
-- lifting -------------------------------------------------------------

type Binary = Ptr CArb -> Ptr CArb -> Ptr CArb -> CLong -> IO ()
type Cmp = Ptr CArb -> Ptr CArb -> IO CInt
type Function = Ptr CArb -> Ptr CArb -> IO ()

lift2 :: forall n. KnownNat n => Binary -> RF n -> RF n -> RF n
lift2 f (RF a) (RF b) = unsafePerformIO $ do
  let prec = fromInteger $ natVal (Proxy :: Proxy n)
  c <- newArb
  withArb a $ \a ->
    withArb b $ \b ->
      withArb c $ \c ->
        f c a b (CLong prec)
  return (RF c)

lift1 :: forall n. KnownNat n => Function -> RF n -> RF n
lift1 f (RF x) = unsafePerformIO $ do
  y <- newArb
  withArb x $ \x -> withArb y $ \y -> f y x
  return (RF y)
  
lift0 f x = RF $ unsafePerformIO $ fst <$> withNewArb (`f` x)
  
liftF1 :: forall n. KnownNat n =>
  (Ptr CArb -> Ptr CArb -> CLong -> IO ()) -> RF n -> RF n
liftF1 f (RF x) = unsafePerformIO $ do
  let prec = fromInteger $ natVal (Proxy :: Proxy n)
  y <- newArb
  withArb x $ \x -> withArb y $ \y -> f y x (CLong prec)
  return (RF y)

liftCmp :: forall n. KnownNat n => Cmp -> RF n -> RF n -> Bool
liftCmp f (RF x) (RF y) = unsafePerformIO $ do
  (_, (_, cmp)) <- withArb x $ \x -> withArb y $ \y -> f x y
  return (cmp == 1)

liftProp :: forall n. KnownNat n => (Ptr CArb -> IO CInt) -> RF n -> Bool
liftProp f (RF x)  = unsafePerformIO $ do
  (_, prop) <- withArb x $ \x -> f x
  return (prop == 1)

liftConstant :: forall n. KnownNat n => (Ptr CArb -> CLong -> IO ()) -> RF n
liftConstant f = RF $ unsafePerformIO $ do
  let prec = fromInteger $ natVal (Proxy :: Proxy n)
  fst <$> withNewArb (`f` CLong prec)

class Special a where
  gamma :: a -> a
  digamma :: a -> a
  lgamma :: a -> a
  zeta :: a -> a
  erf :: a -> a
  airy :: a -> (a, a, a, a)
  airyZeros :: Fmpz -> (a, a, a, a)
  besselJ :: a -> a -> a
  besselY :: a -> a -> a
  besselI :: a -> a -> a
  besselK :: a -> a -> a
  modj :: a -> a
  modjq :: a -> a
  modeta :: a -> a
  modetaq :: a -> a
  modlambda :: a -> a
  modlambdaq :: a -> a
  ellipp :: a -> a -> a
  ellipzeta :: a -> a -> a
  ellipsigma :: a -> a -> a
  barnesg :: a -> a
  agm :: a -> a -> a
  fresnels :: a -> a
  fresnelc :: a -> a
