module QuantLib.Time.DayCounter
        ( module QuantLib.Time.DayCounter
        ) where

import QuantLib.Time.Date
import Data.Time.Calendar

-- | Day counter type class
class DayCounter m where
        -- | Name of day counter
        dcName          :: m->String
        -- | Number of business days inbetween
        dcCount         :: m->Day->Day->Int
        -- | Year fraction
        dcYearFraction  :: m->Day->Day->Double

{-
data SimpleDayCounter = SimpleDayCounter

instance DayCounter SimpleDayCounter where
        dcName _        = "Simple"
        dcCount         = undefined
        dcYearFraction  = undefined
-}

-- | Thirty day counters as in QuantLib
data Thirty360 = ThirtyUSA | ThirtyEuropean | ThirtyItalian

instance DayCounter Thirty360 where
        dcName :: Thirty360 -> String
dcName Thirty360
ThirtyUSA        = String
"Thirty USA"
        dcName Thirty360
ThirtyEuropean   = String
"Thirty Euro"
        dcName Thirty360
ThirtyItalian    = String
"Thirty Italian"

        dcYearFraction :: Thirty360 -> Day -> Day -> Double
dcYearFraction  Thirty360
dc Day
fromDate Day
toDate = Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Thirty360 -> Day -> Day -> Int
forall m. DayCounter m => m -> Day -> Day -> Int
dcCount Thirty360
dc Day
fromDate Day
toDate) Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
360.0

        dcCount :: Thirty360 -> Day -> Day -> Int
dcCount Thirty360
ThirtyUSA Day
fd Day
td = Int
360Int -> Int -> Int
forall a. Num a => a -> a -> a
*(Int
yy2Int -> Int -> Int
forall a. Num a => a -> a -> a
-Int
yy1) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
30Int -> Int -> Int
forall a. Num a => a -> a -> a
*(Int
mm2Int -> Int -> Int
forall a. Num a => a -> a -> a
-Int
mm1Int -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Int
30Int -> Int -> Int
forall a. Num a => a -> a -> a
-Int
dd1) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int -> Int -> Int
forall a. Ord a => a -> a -> a
min Int
30 Int
dd2
                where   (Int
yy1, Int
mm1, Int
dd1) = Day -> (Int, Int, Int)
intGregorian Day
fd
                        (Int
yy2, Int
m2, Int
d2)   = Day -> (Int, Int, Int)
intGregorian Day
td
                        (Int
dd2, Int
mm2)      = Int -> Int -> Int -> (Int, Int)
forall {a} {a} {b}.
(Ord a, Num a, Num a, Num b, Eq a) =>
a -> a -> b -> (a, b)
adjust Int
dd1 Int
d2 Int
m2
                        adjust :: a -> a -> b -> (a, b)
adjust a
x1 a
x2 b
z2
                                | a
x2 a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
31 Bool -> Bool -> Bool
&& a
x1 a -> a -> Bool
forall a. Ord a => a -> a -> Bool
< a
30   = (a
1, b
z2b -> b -> b
forall a. Num a => a -> a -> a
+b
1)
                                | Bool
otherwise             = (a
x2, b
z2)


        dcCount Thirty360
ThirtyEuropean Day
fd Day
td = Int
360Int -> Int -> Int
forall a. Num a => a -> a -> a
*(Int
yy2Int -> Int -> Int
forall a. Num a => a -> a -> a
-Int
yy1) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
30Int -> Int -> Int
forall a. Num a => a -> a -> a
*(Int
m2Int -> Int -> Int
forall a. Num a => a -> a -> a
-Int
m1Int -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Int
30Int -> Int -> Int
forall a. Num a => a -> a -> a
-Int
d1) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int -> Int -> Int
forall a. Ord a => a -> a -> a
min Int
30 Int
d2
                where   (Int
yy1, Int
m1, Int
d1)    = Day -> (Int, Int, Int)
intGregorian Day
fd
                        (Int
yy2, Int
m2, Int
d2)    = Day -> (Int, Int, Int)
intGregorian Day
td

        dcCount Thirty360
ThirtyItalian Day
fd Day
td = Int
360Int -> Int -> Int
forall a. Num a => a -> a -> a
*(Int
yy2Int -> Int -> Int
forall a. Num a => a -> a -> a
-Int
yy1) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
30Int -> Int -> Int
forall a. Num a => a -> a -> a
*(Int
mm2Int -> Int -> Int
forall a. Num a => a -> a -> a
-Int
mm1Int -> Int -> Int
forall a. Num a => a -> a -> a
-Int
1) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Int
30Int -> Int -> Int
forall a. Num a => a -> a -> a
-Int
dd1) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int -> Int -> Int
forall a. Ord a => a -> a -> a
min Int
30 Int
dd2
                where   (Int
yy1, Int
mm1, Int
d1)   = Day -> (Int, Int, Int)
intGregorian Day
fd
                        (Int
yy2, Int
mm2, Int
d2)   = Day -> (Int, Int, Int)
intGregorian Day
td
                        dd1 :: Int
dd1              = Int -> Int -> Int
forall {a} {a}. (Ord a, Num a, Num a, Eq a) => a -> a -> a
adjust Int
d1 Int
mm1
                        dd2 :: Int
dd2              = Int -> Int -> Int
forall {a} {a}. (Ord a, Num a, Num a, Eq a) => a -> a -> a
adjust Int
d2 Int
mm2
                        adjust :: a -> a -> a
adjust a
x1 a
z1
                                | a
z1 a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
2 Bool -> Bool -> Bool
&& a
x1 a -> a -> Bool
forall a. Ord a => a -> a -> Bool
> a
27    = a
30
                                | Bool
otherwise             = a
x1

intGregorian ::  Day -> (Int, Int, Int)
intGregorian :: Day -> (Int, Int, Int)
intGregorian Day
date = (Year -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Year
y, Int
m, Int
d)
        where   (Year
y, Int
m, Int
d) = Day -> (Year, Int, Int)
toGregorian Day
date