ac-library-hs-1.1.0.0: benchmarks/BenchLib/MulMod/BarrettWideWord.hs
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RecordWildCards #-}
-- | Fast modular multiplication by barrett reduction.
module BenchLib.MulMod.BarrettWideWord
( Barrett,
new32,
new64,
umod,
mulMod,
)
where
-- TODO: Use MagicHash?
import Data.WideWord.Word128 (Word128 (..))
import Data.Word (Word32, Word64)
-- | Fast modular multiplication by barrett reduction.
-- Reference: https://en.wikipedia.org/wiki/Barrett_reduction
data Barrett = Barrett
{ -- TODO: should we have it as `Word64`?
mBarrett :: {-# UNPACK #-} !Word32,
imBarrett :: {-# UNPACK #-} !Word64
}
-- | Creates a `Barrett` for modulus value \(m\) of type `Word32`.
new32 :: Word32 -> Barrett
new32 m = Barrett m $ maxBound @Word64 `div` (fromIntegral m :: Word64) + 1
-- | Creates a `Barrett` for modulus value \(m\) of type `Word64`.
new64 :: Word64 -> Barrett
new64 m = Barrett (fromIntegral m) $ maxBound @Word64 `div` m + 1
-- | Retrieves the modulus \(m\).
umod :: Barrett -> Word32
umod Barrett {mBarrett} = mBarrett
-- | Calculates \(a \cdot b \bmod m\).
mulMod :: Barrett -> Word64 -> Word64 -> Word64
mulMod Barrett {..} a b =
let z :: Word64 = a * b
x :: Word64 = word128Hi64 ((fromIntegral z :: Word128) * (fromIntegral imBarrett :: Word128))
y :: Word64 = x * fromIntegral mBarrett
in fromIntegral z - y + if fromIntegral z < y then fromIntegral mBarrett else 0