packages feed

tz-0.0.0.1: benchmarks/benchGreg.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE TemplateHaskell #-}

module Main where

import Criterion.Main
import Control.Lens
import Data.Bits
import Data.Time
import qualified Data.Thyme.Calendar.OrdinalDate as Th
import qualified Data.Thyme as Th
import Data.Time.Calendar.OrdinalDate

data YD = YD {
  _ydYear :: {-# UNPACK #-} !Int,
  _ydDay  :: {-# UNPACK #-} !Int
  } deriving (Show)

makeLenses ''YD

toOrdinalDate' :: Int -> YD
{-# INLINE toOrdinalDate' #-}
toOrdinalDate' !day = YD (fromIntegral y) (fromIntegral d)
  where
    (!y,!d) = toOrdinalDate $ ModifiedJulianDay $ fromIntegral day

toOrdinalDateTh :: Int -> YD
{-# INLINE toOrdinalDateTh #-}
toOrdinalDateTh !day = YD y d
  where
    Th.OrdinalDate y d = Th.ModifiedJulianDay day ^. Th.ordinalDate

toOrdinalDateI :: Int -> YD
toOrdinalDateI !mjd = YD year yd
  where
    a = mjd + 678575
    quadcent = div a 146097
    b = mod a 146097
    cent = min (div b 36524) 3
    c = b - (cent * 36524)
    quad = div c 1461
    d = mod c 1461
    y = min (div d 365) 3
    yd = d - (y * 365) + 1
    year = quadcent * 400 + cent * 100 + quad * 4 + y + 1

toOrdinalDateDivMod :: Int -> YD
{-# NOINLINE toOrdinalDateDivMod #-}
toOrdinalDateDivMod !mjd = YD year yd
  where
    a = mjd + 678575
    (quadcent,b) = a `divMod` 146097
    cent = min (b `quot` 36524) 3
    c = b - (cent * 36524)
    (!quad,!d) = c `quotRem` 1461
    y = min (d `quot` 365) 3
    yd = d - (y * 365) + 1
    year = quadcent * 400 + cent * 100 + quad * 4 + y + 1

--------------------------------------------------------------------------------

isLeapYearI :: Int -> Bool
{-# INLINABLE isLeapYearI #-}
isLeapYearI !y = y .&. 3 == 0  &&  (r100 /= 0 || d100 .&. 3 == 0)
  where
    (d100,r100) = y `quotRem` 100

countLeapYears :: Int -> Int
{-# INLINE countLeapYears #-}
countLeapYears !ys = ys `shiftR` 2  -  cs  +  cs `shiftR` 2
  where
    cs = ys `quot` 100

toOrdinalB0 :: Int -> YD
{-# INLINE toOrdinalB0 #-}
toOrdinalB0 b0 = res
  where
    (y, r) = (400 * b0) `quotRem` 146097
    ls = countLeapYears y
    res = if r < 146097 - 400
          then YD y (b0 - 365*y - ls)
          else let y' = if isLeapYearI (y+1) then y else y+1
               in YD y' (b0 - 365*y' - ls)

toOrdinalPr :: Int -> YD
{-# INLINABLE toOrdinalPr #-}
toOrdinalPr !mjd | b0 >= 0 = toOrdinalB0 b0
                 | otherwise = toOrdinalB0 m & ydYear +~ d * 400
  where
    b0 = mjd + 678575
    (d,m) = b0 `divMod` 146097

test :: Integer -> Int -> Int -> IO ()
test y m md = do
  let d = fromIntegral $ toModifiedJulianDay $ fromGregorian y m md
  print $ d + 678575
  print $ toOrdinalDate' d
  let YD y' yd = toOrdinalPr d
  print $ YD (y'+1) (yd+1)


benchmarks :: [Benchmark]
benchmarks = [
  bgroup "orig" [
     bench "toOrdinalDate" $ nf toOrdinalDate d1,
     bench "toOrdinalDate'" $ whnf toOrdinalDate' d1i
     ],
  bgroup "thyme" [
     bench "toOrdinalDateTh" $ whnf toOrdinalDateTh d1i
     ],
  bgroup "optim" [
     bench "directCopy" $ whnf toOrdinalDateI d1i,
     bench "divModQuotRem" $ whnf toOrdinalDateDivMod d1i,
     bench "divModPr" $ whnf toOrdinalPr d1i
     ]
  ]
  where
    d1 = fromGregorian 2014 3 25
    d1i = fromIntegral $ toModifiedJulianDay d1

main :: IO ()
main = do
  defaultMain benchmarks