{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
module ProductMixAuction.Types
(
Units(..)
, Price(..)
, TweakedPrice(..)
, TradeOff(..)
, Ratio(..)
, unitsToDouble
, tweakedPriceToDouble
, tradeOffToDouble
, doubleToUnits
, Good
, B(..)
, G(..)
, BidderName(..)
, mkBidderName
, ScaleFactor(..)
, roundToScale
, floorToScale
, ceilingToScale
, scaleDistance
, showUnits
, arbitraryUnits
, jsonOptions
, dropPrefix
, Verbosity(..)
, ifDebug
, debugPutStrLn
, debugPrint
) where
import Control.Monad
import Data.Aeson.Types as A hiding (parseField)
import Data.Csv (FromField(..), ToField(..), FromRecord(..))
import Data.Default.Class
import Data.List
import qualified Data.Text as T
import GHC.Generics
import Numeric
import System.Random (Random)
import qualified Data.Vector as V
import qualified Test.QuickCheck as QC
dropPrefix :: String -> (String -> String)
dropPrefix pref s
| pref `isPrefixOf` s = drop (length pref) s
| otherwise = error $ "dropPrefix: " ++ show pref ++
" is not a prefix of " ++ show s
jsonOptions :: String -> A.Options
jsonOptions pref = A.defaultOptions
{ A.fieldLabelModifier = dropPrefix pref
, A.sumEncoding = A.ObjectWithSingleField
, A.omitNothingFields = True
, A.unwrapUnaryRecords = True }
newtype Units = Units { _Units :: Double }
deriving ( QC.Arbitrary, Enum, Eq, Floating, Fractional, FromJSON, Generic
, Num, Ord, Random, Read, Real, RealFloat, RealFrac, Show
, ToField, ToJSON
)
instance Default Units where def = 1
instance FromField Units where
parseField "" = pure 0
parseField s = doubleToUnits <$> parseField s
newtype Price = Price { _Price :: Int }
deriving (QC.Arbitrary, Enum, Eq, Integral, Ord, Read, Real, Show, Num, ToField, Random, ToJSON, FromJSON, Generic, Default)
instance Bounded Price where
minBound = 0
maxBound = Price maxBound
instance FromField Price where
parseField "" = pure 0
parseField s = Price <$> parseField s
instance FromRecord Price where
parseRecord v =
case V.toList v of
[p] -> parseField p
_ -> mzero
newtype TweakedPrice = TweakedPrice { _TweakedPrice :: Double }
deriving (Enum, Eq, Fractional, Ord, Read, Real, RealFrac, Show, Num, ToJSON, FromJSON, Generic, Default)
newtype TradeOff = TradeOff { _TradeOff :: Double }
deriving (Enum, Eq, Num, Ord, Read, Show, ToJSON, FromJSON, Generic)
instance Default TradeOff where def = 1
instance ToField TradeOff where
toField = toField . tradeOffToDouble
instance FromField TradeOff where
parseField "" = pure def
parseField s = doubleToTradeOff <$> parseField s
newtype Ratio = Ratio { _Ratio :: Double }
deriving (Enum, Eq, Fractional, Num, Ord, Read, Show, ToJSON, FromJSON, Generic, Default)
instance QC.Arbitrary Ratio where
arbitrary = Ratio <$> QC.choose (0, 1)
unitsToDouble :: Units -> Double
unitsToDouble = _Units
tweakedPriceToDouble :: TweakedPrice -> Double
tweakedPriceToDouble = _TweakedPrice
tradeOffToDouble :: TradeOff -> Double
tradeOffToDouble = _TradeOff
doubleToUnits :: Double -> Units
doubleToUnits = Units
doubleToTradeOff :: Double -> TradeOff
doubleToTradeOff = TradeOff
newtype ScaleFactor = ScaleFactor { _ScaleFactor :: Int }
deriving (Eq, Read, Show, Num, ToJSON, FromJSON, Generic)
instance Default ScaleFactor where
def = ScaleFactor 1
roundToScale :: RealFrac a => ScaleFactor -> a -> a
roundToScale (ScaleFactor s) = (/e) . fromInteger . round . (* e)
where
e = 10 ^ s
floorToScale :: RealFrac a => ScaleFactor -> a -> a
floorToScale (ScaleFactor s) = (/e) . fromInteger . floor . (* e)
where
e = 10 ^ s
ceilingToScale :: RealFrac a => ScaleFactor -> a -> a
ceilingToScale (ScaleFactor s) = (/e) . fromInteger . ceiling . (* e)
where
e = 10 ^ s
scaleDistance :: ScaleFactor -> Double
scaleDistance (ScaleFactor s) = 1 / 10^s
showUnits :: ScaleFactor -> Units -> String
showUnits (ScaleFactor sf) u = showFFloat (Just sf) (unitsToDouble u) ""
type Good = G
newtype G = G { _good_index :: Int }
deriving (QC.Arbitrary, Eq, Ord, Show, Read, Enum, ToField, ToJSONKey, FromJSONKey, ToJSON, FromJSON, Generic)
newtype B = B { _bid_index :: Int }
deriving (QC.Arbitrary, Eq, Ord, Show, Read, Enum, ToField, ToJSONKey, FromJSONKey, ToJSON, FromJSON, FromField, Generic)
instance Default G where def = G 1
instance Default B where def = B 1
newtype BidderName = BidderName { _BidderName :: T.Text }
deriving (FromField, ToField, FromJSON, FromJSONKey, ToJSON, ToJSONKey, Generic, Eq, Ord, Show, Read)
mkBidderName :: Int -> BidderName
mkBidderName i = BidderName (T.pack (show i))
instance QC.Arbitrary BidderName where
arbitrary = mkBidderName <$> QC.arbitrary
arbitraryUnits :: Int -> Int -> QC.Gen Units
arbitraryUnits min_units max_units =
Units . fromIntegral <$> QC.choose (min_units, max_units)
data Verbosity = Silent
| Debug
deriving (Bounded, Enum, Eq, Ord, Generic, Show)
instance Default Verbosity where
def = Silent
instance FromJSON Verbosity
where parseJSON = genericParseJSON $ jsonOptions ""
instance ToJSON Verbosity
where toJSON = genericToJSON $ jsonOptions ""
instance QC.Arbitrary Verbosity where
arbitrary = QC.arbitraryBoundedEnum
ifDebug :: Verbosity -> IO () -> IO ()
ifDebug v = when (v >= Debug)
debugPutStrLn :: Verbosity -> String -> String -> IO ()
debugPutStrLn v k s = ifDebug v (putStrLn k >> putStrLn s)
debugPrint :: Show a => Verbosity -> String -> a -> IO ()
debugPrint v k x = debugPutStrLn v k (show x)