{-#LANGUAGE GeneralizedNewtypeDeriving #-}
module ProductMixAuction.DotBids.Frac2
where

import Data.Ratio (numerator, denominator)
import Test.QuickCheck (Arbitrary)

-- | Represents an arbitrary-size fixed-point value with a fractional part of
-- exactly 1 bit.  In other words, this type can accurately represent multiples
-- of 0.5.
newtype Frac2 = Frac2 { unFrac2 :: Integer }
  deriving (Arbitrary, Enum, Eq, Ord, Read, Real)

toFrac2 :: Integer -> Frac2
toFrac2 x = Frac2 (x * 2)

fromFrac2 :: Frac2 -> Maybe Integer
fromFrac2 (Frac2 x)
  | even x = Just (x `quot` 2)
  | otherwise = Nothing

unsafeFromFrac2 :: Frac2 -> Integer
unsafeFromFrac2 (Frac2 x) = x `quot` 2

instance Num Frac2 where
  Frac2 a + Frac2 b = Frac2 (a + b)
  Frac2 a - Frac2 b = Frac2 (a - b)
  Frac2 a * Frac2 b = Frac2 ((a * b) `quot` 2)
  negate (Frac2 a) = Frac2 (negate a)
  abs (Frac2 a) = Frac2 (abs a)
  signum (Frac2 a) = Frac2 (signum a)
  fromInteger = toFrac2

instance Fractional Frac2 where
  Frac2 a / Frac2 b = Frac2 (quot (2 * a) b)
  recip (Frac2 a) = Frac2 (quot 4 a)
  fromRational r =
    let n = numerator r
        d = denominator r
    in Frac2 (2 * n `quot` d)

instance Show Frac2 where
  show = showFrac2

showFrac2 :: Frac2 -> String
showFrac2 (Frac2 x) =
  show intpart ++ "." ++ if fracpart == 0 then "0" else "5"
  where
    (intpart, fracpart) = x `quotRem` 2