module Control.Concurrent.Chan.Unagi.Constants
    where

-- Constants for boxed and unboxed unagi.

import Data.Bits
import Control.Exception(assert)

divMod_sEGMENT_LENGTH :: Int -> (Int,Int)
{-# INLINE divMod_sEGMENT_LENGTH #-}
divMod_sEGMENT_LENGTH n = let d = n `unsafeShiftR` lOG_SEGMENT_LENGTH
                              m = n .&. sEGMENT_LENGTH_MN_1
                           in d `seq` m `seq` (d,m)

-- Nexttant for now: back-of-envelope considerations:
--   - making most of constant factor for cloning array of *any* size
--   - make most of overheads of moving to the next segment, etc.
--   - provide enough runway for creating next segment when 32 simultaneous writers 
--   - the larger this the larger one-time cost for the lucky writer
--   - as arrays collect in heap, performance might suffer, so bigger arrays
--     give us a constant factor edge there. see:
--       http://stackoverflow.com/q/23462004/176841
--
sEGMENT_LENGTH :: Int
{-# INLINE sEGMENT_LENGTH #-}
sEGMENT_LENGTH = 1024 -- NOTE: THIS MUST REMAIN A POWER OF 2!

-- Number of reads on which to spin for new segment creation.
-- Back-of-envelope (time_to_create_new_segment / time_for_read_IOref) + margin.
-- See usage site.
--
-- NOTE: this was calculated for boxed Unagi, but it probably doesn't make a
-- measurable difference that we use it for Unagi.Unboxed too.
nEW_SEGMENT_WAIT :: Int
nEW_SEGMENT_WAIT = round (((14.6::Float) + 0.3*fromIntegral sEGMENT_LENGTH) / 3.7) + 10

lOG_SEGMENT_LENGTH :: Int
lOG_SEGMENT_LENGTH =
    let x = 10  -- ...pre-computed from...
     in assert (x == (round $ logBase (2::Float) $ fromIntegral sEGMENT_LENGTH))
         x

sEGMENT_LENGTH_MN_1 :: Int
sEGMENT_LENGTH_MN_1 = sEGMENT_LENGTH - 1