packages feed

ac-library-hs-1.2.2.1: src/AtCoder/Internal/Barrett.hs

{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RecordWildCards #-}

-- | Fast modular multiplication for `Word32` using barrett reduction.
-- Reference: https://en.wikipedia.org/wiki/Barrett_reduction
--
-- ==== __Example__
-- >>> let bt = new32 10 -- mod 10
-- >>> umod bt
-- 10
--
-- >>> mulMod bt 7 7
-- 9
--
-- @since 1.0.0.0
module AtCoder.Internal.Barrett
  ( -- * Barrett
    Barrett,

    -- * Constructor
    new32,
    new64,

    -- * Accessor
    umod,

    -- * Barrett reduction
    mulMod,
  )
where

import Data.WideWord.Word128 (Word128 (..))
import Data.Word (Word32, Word64)

-- | Fast modular multiplication using barrett reduction.
-- Reference: https://en.wikipedia.org/wiki/Barrett_reduction
--
-- @since 1.0.0.0
data Barrett = Barrett
  { mBarrett :: {-# UNPACK #-} !Word32,
    imBarrett :: {-# UNPACK #-} !Word64
  }
  deriving
    ( -- | @since 1.0.0.0
      Eq,
      -- | @since 1.0.0.0
      Show
    )

-- | Creates a `Barrett` for a modulus value \(m\) of type `Word32` value.
--
-- @since 1.0.0.0
{-# INLINE new32 #-}
new32 :: Word32 -> Barrett
new32 m = Barrett m $ maxBound @Word64 `div` (fromIntegral m :: Word64) + 1

-- | Creates a `Barrett` for a modulus value \(m\) of type `Word64` value.
--
-- @since 1.0.0.0
{-# INLINE new64 #-}
new64 :: Word64 -> Barrett
new64 m = Barrett (fromIntegral m) $ maxBound @Word64 `div` m + 1

-- | Retrieves the modulus \(m\).
--
-- @since 1.0.0.0
{-# INLINE umod #-}
umod :: Barrett -> Word32
umod Barrett {mBarrett} = mBarrett

-- | Calculates \(a b \bmod m\).
--
-- @since 1.0.0.0
{-# INLINE mulMod #-}
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