module Data.Fraction (
Fraction,
fromFactor,
fromPercentage,
fromNumber,
toFactor,
toPercentage,
toNumber
) where
import Data.Semigroup as Semigroup
import Data.Monoid as Monoid
newtype Fraction = Fraction Double
instance Semigroup Fraction where
Fraction factor1 `append` Fraction factor2 = Fraction (factor1 * factor2)
instance Monoid Fraction where
mempty = Fraction 1
mappend = append
fromFactor :: (Real real) => real -> Fraction
fromFactor = fromNumber (0,1)
fromPercentage :: (Real real) => real -> Fraction
fromPercentage = fromNumber (0,100)
fromNumber :: (Real real) => (real,real) -> real -> Fraction
fromNumber (lower,upper) real = checkInterval (lower,upper) `seq`
if lower <= real && real <= upper
then Fraction $
(realToFrac real fracMin) / (fracMax fracMin)
else error "fraction: real out of bounds" where
fracMin = realToFrac lower
fracMax = realToFrac upper
toFactor :: Fraction -> Double
toFactor (Fraction factor) = factor
toPercentage :: Fraction -> Double
toPercentage (Fraction factor) = factor * 100
toNumber :: (Double,Double) -> Fraction -> Double
toNumber (lower,upper) (Fraction factor) = checkInterval (lower,upper) `seq`
factor * (upper lower) + lower
checkInterval :: (Real real) => (real,real) -> ()
checkInterval (lower,upper) | lower < upper = ()
| otherwise = error "fraction: no proper interval"