{-# LANGUAGE DeriveDataTypeable #-}
module Data.RealNumber.Rational(RealNumber(..)
, AsFixed(..), asFixed
, toFixed, fromFixed
) where
import Data.Data
import Data.Fixed
import Data.Hashable
import Data.List (dropWhileEnd)
import GHC.Generics (Generic(..))
import GHC.TypeLits
import Test.QuickCheck(Arbitrary(..))
newtype RealNumber (p :: Nat) = RealNumber Rational
deriving (Eq,Ord,Data,Num,Fractional,Real,RealFrac,Generic,Hashable)
data NatPrec (p :: Nat) = NatPrec
instance KnownNat p => HasResolution (NatPrec p) where
resolution _ = 10 ^ (1 + natVal (NatPrec @p))
instance KnownNat p => Show (RealNumber p) where
show r = case asFixed r of
Exact p -> dropWhileEnd (== '.') . dropWhileEnd (== '0') . show $ p
Lossy p -> (<> "~") . show $ p
instance KnownNat p => Read (RealNumber p) where
readsPrec i = map wrap . readsPrec @(Fixed (NatPrec p)) i
where
wrap (RealNumber . realToFrac -> x,s') = case s' of
'~':s'' -> (x,s'')
_ -> (x,s')
instance KnownNat p => Arbitrary (RealNumber p) where
arbitrary = fromFixed <$> arbitrary
data AsFixed p = Exact !(Fixed p) | Lossy !(Fixed p) deriving (Show,Eq)
toFixed :: KnownNat p => RealNumber p -> Fixed (NatPrec p)
toFixed = realToFrac
fromFixed :: KnownNat p => Fixed (NatPrec p) -> RealNumber p
fromFixed = realToFrac
asFixed :: KnownNat p => RealNumber p -> AsFixed (NatPrec p)
asFixed r = let p = toFixed r in if r == fromFixed p then Exact p else Lossy p