{-# LANGUAGE Safe #-}
{-# OPTIONS_GHC -fno-warn-type-defaults #-}
module Data.Quantity (
renderNum,
renderNums,
parseNum,
parseNumInt,
quantifyNum,
quantifyNums,
SizeOpts(..),
binaryOpts,
siOpts
)
where
import safe Data.Char ( toLower )
import safe Data.List (find)
import safe Text.Printf ( printf )
data SizeOpts = SizeOpts { SizeOpts -> Int
base :: Int,
SizeOpts -> Int
powerIncr :: Int,
SizeOpts -> Int
firstPower :: Int,
SizeOpts -> String
suffixes :: String
}
binaryOpts :: SizeOpts
binaryOpts :: SizeOpts
binaryOpts = SizeOpts {base :: Int
base = Int
2,
firstPower :: Int
firstPower = Int
0,
suffixes :: String
suffixes = String
" KMGTPEZY",
powerIncr :: Int
powerIncr = Int
10}
siOpts :: SizeOpts
siOpts :: SizeOpts
siOpts = SizeOpts {base :: Int
base = Int
10,
firstPower :: Int
firstPower = -Int
24,
suffixes :: String
suffixes = String
"yzafpnum kMGTPEZY",
powerIncr :: Int
powerIncr = Int
3}
quantifyNum :: (Ord a, Real a, Floating b, Ord b) => SizeOpts -> a -> (b, Char)
quantifyNum :: forall a b.
(Ord a, Real a, Floating b, Ord b) =>
SizeOpts -> a -> (b, Char)
quantifyNum SizeOpts
opts a
n = (\([b]
x, Char
s) -> ([b] -> b
forall a. HasCallStack => [a] -> a
head [b]
x, Char
s)) (([b], Char) -> (b, Char)) -> ([b], Char) -> (b, Char)
forall a b. (a -> b) -> a -> b
$ SizeOpts -> [a] -> ([b], Char)
forall a b.
(Ord a, Real a, Floating b, Ord b) =>
SizeOpts -> [a] -> ([b], Char)
quantifyNums SizeOpts
opts [a
n]
quantifyNums :: (Ord a, Real a, Floating b, Ord b) => SizeOpts -> [a] -> ([b], Char)
quantifyNums :: forall a b.
(Ord a, Real a, Floating b, Ord b) =>
SizeOpts -> [a] -> ([b], Char)
quantifyNums SizeOpts
_ [] = String -> ([b], Char)
forall a. HasCallStack => String -> a
error String
"Attempt to use quantifyNums on an empty list"
quantifyNums SizeOpts
opts (a
headnum:[a]
xs) =
((a -> b) -> [a] -> [b]
forall a b. (a -> b) -> [a] -> [b]
map (\a
n -> a -> b
forall {a} {p}. (Real p, Floating a) => p -> a
procnum a
n) (a
headnuma -> [a] -> [a]
forall a. a -> [a] -> [a]
:[a]
xs), Char
suffix)
where number :: Double
number = case Rational -> Double
forall a. Fractional a => Rational -> a
fromRational (Rational -> Double) -> (a -> Rational) -> a -> Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Rational
forall a. Real a => a -> Rational
toRational (a -> Double) -> a -> Double
forall a b. (a -> b) -> a -> b
$ a
headnum of
Double
0 -> Double
1
Double
x -> Double
x
incrList :: [Int]
incrList = (Int -> Int) -> [Int] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map Int -> Int
idx2pwr [Int
0..String -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (SizeOpts -> String
suffixes SizeOpts
opts) Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1]
incrIdxList :: [(Int, Integer)]
incrIdxList = [Int] -> [Integer] -> [(Int, Integer)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Int]
incrList [Integer
0..]
idx2pwr :: Int -> Int
idx2pwr Int
i = Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
* SizeOpts -> Int
powerIncr SizeOpts
opts Int -> Int -> Int
forall a. Num a => a -> a -> a
+ SizeOpts -> Int
firstPower SizeOpts
opts
finderfunc :: (a, b) -> Bool
finderfunc (a
x, b
_) = (Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Double) -> Int -> Double
forall a b. (a -> b) -> a -> b
$ SizeOpts -> Int
base SizeOpts
opts) Double -> Double -> Double
forall a. Floating a => a -> a -> a
** (a -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
x)
Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
<= (Double -> Double
forall a. Num a => a -> a
abs Double
number)
(Int
usedexp, Integer
expidx) =
case ((Int, Integer) -> Bool)
-> [(Int, Integer)] -> Maybe (Int, Integer)
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (Int, Integer) -> Bool
forall {a} {b}. Integral a => (a, b) -> Bool
finderfunc ([(Int, Integer)] -> [(Int, Integer)]
forall a. [a] -> [a]
reverse [(Int, Integer)]
incrIdxList) of
Just (Int, Integer)
x -> (Int, Integer)
x
Maybe (Int, Integer)
Nothing -> [(Int, Integer)] -> (Int, Integer)
forall a. HasCallStack => [a] -> a
head [(Int, Integer)]
incrIdxList
suffix :: Char
suffix = (SizeOpts -> String
suffixes SizeOpts
opts String -> Int -> Char
forall a. HasCallStack => [a] -> Int -> a
!! (Integer -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
expidx))
procnum :: p -> a
procnum p
n = (Rational -> a
forall a. Fractional a => Rational -> a
fromRational (Rational -> a) -> (p -> Rational) -> p -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. p -> Rational
forall a. Real a => a -> Rational
toRational (p -> a) -> p -> a
forall a b. (a -> b) -> a -> b
$ p
n) a -> a -> a
forall a. Fractional a => a -> a -> a
/
((Int -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral (SizeOpts -> Int
base SizeOpts
opts) a -> a -> a
forall a. Floating a => a -> a -> a
** (Int -> a
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
usedexp)))
renderNum :: (Ord a, Real a) =>
SizeOpts
-> Int
-> a
-> String
renderNum :: forall a. (Ord a, Real a) => SizeOpts -> Int -> a -> String
renderNum SizeOpts
opts Int
prec a
number =
(String -> Double -> String
forall r. PrintfType r => String -> r
printf (String
"%." String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
prec String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"g") Double
num) String -> String -> String
forall a. [a] -> [a] -> [a]
++ [Char
suffix]
where (Double
num, Char
suffix) = (SizeOpts -> a -> (Double, Char)
forall a b.
(Ord a, Real a, Floating b, Ord b) =>
SizeOpts -> a -> (b, Char)
quantifyNum SizeOpts
opts a
number)::(Double, Char)
renderNums :: (Ord a, Real a) =>
SizeOpts
-> Int
-> [a]
-> [String]
renderNums :: forall a. (Ord a, Real a) => SizeOpts -> Int -> [a] -> [String]
renderNums SizeOpts
opts Int
prec [a]
numbers =
(Double -> String) -> [Double] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Double -> String
forall {t}. PrintfArg t => t -> String
printit [Double]
convnums
where printit :: t -> String
printit t
num =
(String -> t -> String
forall r. PrintfType r => String -> r
printf (String
"%." String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
prec String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"f") t
num) String -> String -> String
forall a. [a] -> [a] -> [a]
++ [Char
suffix]
([Double]
convnums, Char
suffix) =
(SizeOpts -> [a] -> ([Double], Char)
forall a b.
(Ord a, Real a, Floating b, Ord b) =>
SizeOpts -> [a] -> ([b], Char)
quantifyNums SizeOpts
opts [a]
numbers)::([Double], Char)
parseNum :: (Read a, Fractional a) =>
SizeOpts
-> Bool
-> String
-> Either String a
parseNum :: forall a.
(Read a, Fractional a) =>
SizeOpts -> Bool -> String -> Either String a
parseNum SizeOpts
opts Bool
insensitive String
inp =
case ReadS a
forall a. Read a => ReadS a
reads String
inp of
[] -> String -> Either String a
forall a b. a -> Either a b
Left String
"Couldn't parse numeric component of input"
[(a
num, String
"")] -> a -> Either String a
forall a b. b -> Either a b
Right a
num
[(a
num, [Char
suffix])] ->
case Char -> [(Char, Int)] -> Maybe Int
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup (Char -> Char
caseTransformer Char
suffix) [(Char, Int)]
suffixMap of
Maybe Int
Nothing -> String -> Either String a
forall a b. a -> Either a b
Left (String -> Either String a) -> String -> Either String a
forall a b. (a -> b) -> a -> b
$ String
"Unrecognized suffix " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Char -> String
forall a. Show a => a -> String
show Char
suffix
Just Int
power -> a -> Either String a
forall a b. b -> Either a b
Right (a -> Either String a) -> a -> Either String a
forall a b. (a -> b) -> a -> b
$ a
num a -> a -> a
forall a. Num a => a -> a -> a
* Int -> a
forall a. (Read a, Fractional a) => Int -> a
multiplier Int
power
[(a
_, String
suffix)] -> String -> Either String a
forall a b. a -> Either a b
Left (String -> Either String a) -> String -> Either String a
forall a b. (a -> b) -> a -> b
$ String
"Multi-character suffix " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
forall a. Show a => a -> String
show String
suffix
[(a, String)]
_ -> String -> Either String a
forall a b. a -> Either a b
Left String
"Multiple parses for input"
where suffixMap :: [(Char, Int)]
suffixMap = String -> [Int] -> [(Char, Int)]
forall a b. [a] -> [b] -> [(a, b)]
zip ((Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
caseTransformer (String -> String) -> (SizeOpts -> String) -> SizeOpts -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SizeOpts -> String
suffixes (SizeOpts -> String) -> SizeOpts -> String
forall a b. (a -> b) -> a -> b
$ SizeOpts
opts)
((Int -> Int) -> Int -> [Int]
forall a. (a -> a) -> a -> [a]
iterate (Int -> Int -> Int
forall a. Num a => a -> a -> a
+ (SizeOpts -> Int
powerIncr SizeOpts
opts)) (SizeOpts -> Int
firstPower SizeOpts
opts))
caseTransformer :: Char -> Char
caseTransformer Char
x
| Bool
insensitive = Char -> Char
toLower Char
x
| Bool
otherwise = Char
x
multiplier :: (Read a, Fractional a) => Int -> a
multiplier :: forall a. (Read a, Fractional a) => Int -> a
multiplier Int
power =
Rational -> a
forall a. Fractional a => Rational -> a
fromRational (Rational -> a) -> (Double -> Rational) -> Double -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Double -> Rational
forall a. Real a => a -> Rational
toRational (Double -> a) -> Double -> a
forall a b. (a -> b) -> a -> b
$
Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (SizeOpts -> Int
base SizeOpts
opts) Double -> Double -> Double
forall a. Floating a => a -> a -> a
** Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
power
parseNumInt :: (Read a, Integral a) =>
SizeOpts
-> Bool
-> String
-> Either String a
parseNumInt :: forall a.
(Read a, Integral a) =>
SizeOpts -> Bool -> String -> Either String a
parseNumInt SizeOpts
opts Bool
insensitive String
inp =
case (SizeOpts -> Bool -> String -> Either String Double
forall a.
(Read a, Fractional a) =>
SizeOpts -> Bool -> String -> Either String a
parseNum SizeOpts
opts Bool
insensitive String
inp)::Either String Double of
Left String
x -> String -> Either String a
forall a b. a -> Either a b
Left String
x
Right Double
n -> a -> Either String a
forall a b. b -> Either a b
Right (Double -> a
forall b. Integral b => Double -> b
forall a b. (RealFrac a, Integral b) => a -> b
round Double
n)