packages feed

fixed-generic-0.1.0.0: src/Data/Fixed/Generic.hs

{-# LANGUAGE BlockArguments, ScopedTypeVariables, TypeApplications #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

module Data.Fixed.Generic (F(..), showF) where

import GHC.Internal.Read
import GHC.Internal.Text.ParserCombinators.ReadPrec
import GHC.Internal.Text.Read.Lex
import Data.Data
import Data.Fixed

newtype F (a :: k) n = MkF n deriving (Eq, Ord)

tyF :: DataType
tyF = mkDataType "Data.Fixed.Generic.F" [conMkF]

conMkF :: Constr
conMkF = mkConstr tyF "MkF" [] Prefix

instance (Typeable k, Typeable a, Data n) => Data (F (a :: k) n) where
	gfoldl k z (MkF a) = k (z MkF) a
	gunfold k z _ = k (z MkF)
	dataTypeOf _ = tyF
	toConstr _ = conMkF

withResolution :: forall k (a :: k) f . HasResolution a => (Integer -> f) -> f
withResolution foo = foo . resolution $ Proxy @a

resl :: forall a n . (HasResolution a, Num n) => n
resl = fromInteger $ resolution (Proxy @a)

withResl :: forall k (a :: k) n f . (Num n, HasResolution a) => (n -> f) -> f
withResl foo = foo $ resl @a

instance Enum n => Enum (F a n) where
	succ (MkF a) = MkF (succ a)
	pred (MkF a) = MkF (succ a)
	toEnum = MkF . toEnum
	fromEnum (MkF a) = fromEnum a
	enumFrom (MkF a) = fmap MkF $ enumFrom a
	enumFromThen (MkF a) (MkF b) = fmap MkF (enumFromThen a b)
	enumFromTo (MkF a) (MkF b) = fmap MkF (enumFromTo a b)
	enumFromThenTo (MkF a) (MkF b) (MkF c) = fmap MkF (enumFromThenTo a b c)

instance (HasResolution a, Integral n) => Num (F a n) where
	MkF a + MkF b = MkF $ a + b
	MkF a - MkF b = MkF $ a - b
	MkF a * MkF b = MkF $ (a * b) `div` resl @a
	negate (MkF a) = MkF $ negate a
	abs (MkF a) = MkF $ abs a
	signum (MkF a) = fromIntegral $ signum a
	fromInteger i = withResl @_ @a \res -> MkF $ fromInteger i * res

instance (HasResolution a, Integral n) => Real (F a n) where
	toRational (MkF a) = toRational a / toRational (resolution (Proxy @a))

instance (HasResolution a, Integral n) => Fractional (F a n) where
	MkF a / MkF b = MkF $ (a * resl @a) `div` b
	recip (MkF a) = MkF $ (res * res) `div` a where res = resl @a
	fromRational r =
		withResolution @_ @a \res -> MkF (floor (r * (toRational res)))

instance (HasResolution a, Integral n) => RealFrac (F a n) where
	properFraction a = (i, a - fromIntegral i)
		where i = truncate a
	truncate f = truncate (toRational f)
	round f = round (toRational f)
	ceiling f = ceiling (toRational f)
	floor f = floor (toRational f)

chopZeros :: (Show n, Integral n) => n -> String
chopZeros 0 = ""
chopZeros a | a `mod` 10 == 0 = chopZeros (a `div` 10)
chopZeros a = show a

showIntegerZeros :: (Show n, Integral n) => Bool -> Int -> n -> String
showIntegerZeros True _ 0 = ""
showIntegerZeros chopTrailingZeros digits a = replicate (digits - length s) '0' ++ s' where
	s = show a
	s' = if chopTrailingZeros then chopZeros a else s

withDot :: String -> String
withDot "" = ""
withDot s = '.' : s

showF :: forall a n . (HasResolution a, Show n, Integral n) => Bool -> F a n -> String
showF chopTrailingZeros (MkF a) | a < 0 =
	"-" ++ showF chopTrailingZeros (MkF (negate a) :: F a n)
showF chopTrailingZeros (MkF a) =
	show i ++ withDot (showIntegerZeros chopTrailingZeros digits fracNum)
	where
	res = resl @a
	(i, d) = a `divMod` res
	digits = ceiling (logBase 10 (fromIntegral res) :: Double)
	maxnum = 10 ^ digits
	fracNum = divCeil (d * maxnum) res
	divCeil x y = (x + y - 1) `div` y

instance (HasResolution a, Show n, Integral n) => Show (F a n) where
	showsPrec p n = showParen (p > 6 && n < 0) $ showString $ showF False n

convertF :: forall a n . (HasResolution a, Integral n) => Lexeme -> ReadPrec (F a n)
convertF (Number n)
	| Just (i, f) <- numberToFixed e n =
		pure (fromInteger i + (fromInteger f / (10 ^ e)))
		where
		r = resl @a
		e = ceiling (logBase 10 (fromInteger r) :: Double)
convertF _ = pfail

instance (HasResolution a, Integral n) => Read (F a n) where
	readPrec = readNumber convertF
	readListPrec = readListPrecDefault
	readList = readListDefault