packages feed

ac-library-hs-1.5.0.0: src/AtCoder/Extra/ModInt64.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE TypeFamilies #-}

-- | @ModInt@ for 64 bit modulus values with Montgomery modular multiplication.
--
-- ==== Constraints
-- - The modulus value must be an odd number.
--
-- @since 1.2.6.0
module AtCoder.Extra.ModInt64
  ( -- * ModInt64
    ModInt64 (..),

    -- * Constructors

    -- ** Safe constructors
    new,
    new64,

    -- ** Unsafe constructor
    unsafeNew,

    -- * Accessors

    -- ** Modulus value
    modulus,

    -- ** Internal value
    val,
    val64,

    -- * Operators
    pow,
    inv,
  )
where

import AtCoder.Internal.Assert qualified as ACIA
import AtCoder.Extra.Math.Montgomery64 qualified as M64
import Data.Ratio (denominator, numerator)
import Data.Vector.Generic qualified as VG
import Data.Vector.Generic.Mutable qualified as VGM
import Data.Vector.Primitive qualified as P
import Data.Vector.Unboxed qualified as U
import Data.Vector.Unboxed qualified as VU
import Data.Word (Word64)
import GHC.Exts (proxy#)
import GHC.Stack (HasCallStack)
import GHC.TypeNats (KnownNat, natVal')

-- | `Word64` value that treats the modular arithmetic.
--
-- ==== Constraints
-- - The modulus value must be an odd number.
--
-- @since 1.2.6.0
newtype ModInt64 a = ModInt64
  { -- | Montgomery form of the value. Use `val` to retrieve the value.
    --
    -- @since 1.2.6.0
    unModInt64 :: Word64
  }
  deriving
    ( -- | @since 1.2.6.0
      P.Prim
    )

-- | @since 1.2.6.0
instance (KnownNat a) => Eq (ModInt64 a) where
  {-# INLINE (==) #-}
  ModInt64 x == ModInt64 y = M64.eq (fromIntegral (natVal' (proxy# @a))) x y

-- | @since 1.2.6.0
instance (KnownNat a) => Ord (ModInt64 a) where
  {-# INLINE compare #-}
  compare (ModInt64 a) (ModInt64 b) = compare a b

-- | @since 1.2.6.0
instance (KnownNat a) => Read (ModInt64 a) where
  {-# INLINE readsPrec #-}
  readsPrec p s = [(fromInteger x, r) | (!x, !r) <- readsPrec p s]

-- | @since 1.2.6.0
instance (KnownNat a) => Show (ModInt64 a) where
  {-# INLINE show #-}
  show = show . val

-- | \(O(1)\) Creates a `ModInt64` from an `Int` value taking the mod.
--
-- @since 1.2.6.0
{-# INLINE new #-}
new :: forall a. (KnownNat a) => Int -> ModInt64 a
new = ModInt64 . M64.encode (M64.new (proxy# @a)) . fromIntegral . (`mod` m)
  where
    !m = fromIntegral $ natVal' (proxy# @a)

-- | \(O(1)\) Creates a `ModInt64` from a `Word64` value taking the mod.
--
-- @since 1.2.6.0
{-# INLINE new64 #-}
new64 :: forall a. (KnownNat a) => Word64 -> ModInt64 a
new64 = ModInt64 . M64.encode (M64.new (proxy# @a))

-- | \(O(1)\) Creates `ModInt64` from a Montgomery form with no validation.
--
-- @since 1.2.6.0
{-# INLINE unsafeNew #-}
unsafeNew :: (KnownNat a) => Word64 -> ModInt64 a
unsafeNew = ModInt64

-- | \(O(1)\) Retrieve the modulus from a `ModInt64` object.
--
-- ==== Complexity
-- - \(O(1)\)
--
-- @since 1.2.6.0
{-# INLINE modulus #-}
modulus :: forall a. (KnownNat a) => ModInt64 a -> Int
modulus _ = fromIntegral (natVal' (proxy# @a))

-- | \(O(1)\) Returns the internal value as an `Int`.
--
-- ==== Complexity
-- - \(O(1)\)
--
-- @since 1.2.6.0
{-# INLINE val #-}
val :: forall a. (KnownNat a) => ModInt64 a -> Int
val = fromIntegral . val64

-- | \(O(1)\) Returns the internal value as a `Word64`.
--
-- ==== Complexity
-- - \(O(1)\)
--
-- @since 1.2.6.0
{-# INLINE val64 #-}
val64 :: forall a. (KnownNat a) => ModInt64 a -> Word64
val64 (ModInt64 x) = M64.decode (M64.new (proxy# @a)) x

-- | \(O(\log n)\) Returns \(x^n\). The implementation is a bit more efficient than `^`.
--
-- ==== Constraints
-- - \(0 \le n\)
--
-- @since 1.2.6.0
{-# INLINE pow #-}
pow :: forall a. (HasCallStack, KnownNat a) => ModInt64 a -> Int -> ModInt64 a
pow (ModInt64 x) n = ModInt64 $! M64.powMod (M64.new (proxy# @a)) x n

-- TODO: move invMod to Montgomery64
-- TODO: time complexity of `inv`?

-- | Returns \(y\) such that \(xy \equiv 1 \bmod m\) holds.
--
-- ==== Constraints
-- - The value must not be zero.
--
-- @since 1.2.6.0
{-# INLINE inv #-}
inv :: forall a. (HasCallStack, KnownNat a) => ModInt64 a -> ModInt64 a
-- TODO: assert zero division?
inv self = inner (val self) m 1 0
  where
    !_ = ACIA.runtimeAssert (val self /= 0) "AtCoder.Extra.ModInt64.inv: given zero"
    !m = fromIntegral (natVal' (proxy# @a))
    inner x y u v
      | y <= 0 = new u
      | otherwise = inner x' y' u' v'
      where
        x' = y
        y' = x - t * y
        u' = v
        v' = u - t * v
        t = x `div` y

-- https://github.com/NyaanNyaan/library/blob/master/modint/montgomery-modint.hpp
-- constexpr mint inverse() const {
--   int x = get(), y = mod, u = 1, v = 0, t = 0, tmp = 0;
--   while (y > 0) {
--     t = x / y;
--     x -= t * y, u -= t * v;
--     tmp = x, x = y, y = tmp;
--     tmp = u, u = v, v = tmp;
--   }
--   return mint{u};
-- }

-- | @since 1.2.6.0
deriving newtype instance (KnownNat p) => Real (ModInt64 p)

-- | @since 1.2.6.0
instance forall p. (KnownNat p) => Num (ModInt64 p) where
  {-# INLINE (+) #-}
  (ModInt64 !x1) + (ModInt64 !x2) = ModInt64 $! M64.addMod m x1 x2
    where
      !m = fromIntegral (natVal' (proxy# @p))
  {-# INLINE (-) #-}
  (ModInt64 !x1) - (ModInt64 !x2) = ModInt64 $! M64.subMod m x1 x2
    where
      !m = fromIntegral (natVal' (proxy# @p))
  {-# INLINE (*) #-}
  (ModInt64 !x1) * (ModInt64 !x2) = ModInt64 $! M64.mulMod (M64.new (proxy# @p)) x1 x2
  {-# INLINE negate #-}
  negate x = 0 - x
  {-# INLINE abs #-}
  abs = id
  {-# INLINE signum #-}
  signum _ = ModInt64 $ M64.encode (M64.new (proxy# @p)) 1
  -- because the input value can be negative, be sure to take the mod:
  {-# INLINE fromInteger #-}
  fromInteger = ModInt64 . M64.encode (M64.new (proxy# @p)) . fromInteger . (`mod` m)
    where
      !m = toInteger $ natVal' (proxy# @p)

-- | @since 1.2.6.0
instance (KnownNat p) => Bounded (ModInt64 p) where
  {-# INLINE minBound #-}
  minBound = ModInt64 0
  {-# INLINE maxBound #-}
  maxBound = ModInt64 . M64.encode (M64.new (proxy# @p)) $! fromIntegral (natVal' (proxy# @p)) - 1

-- | @since 1.2.6.0
instance (KnownNat p) => Enum (ModInt64 p) where
  {-# INLINE toEnum #-}
  toEnum = new
  {-# INLINE fromEnum #-}
  fromEnum = fromIntegral . val

-- | @since 1.2.6.0
instance (KnownNat p) => Integral (ModInt64 p) where
  {-# INLINE quotRem #-}
  quotRem x y = (x / y, x - x / y * y)
  {-# INLINE toInteger #-}
  toInteger = toInteger . val

-- | @since 1.2.6.0
instance (KnownNat p) => Fractional (ModInt64 p) where
  {-# INLINE recip #-}
  recip = inv
  {-# INLINE fromRational #-}
  fromRational q = fromInteger (numerator q) / fromInteger (denominator q)

-- | @since 1.2.6.0
newtype instance VU.MVector s (ModInt64 a) = MV_ModInt64 (VU.MVector s Word64)

-- | @since 1.2.6.0
newtype instance VU.Vector (ModInt64 a) = V_ModInt64 (VU.Vector Word64)

-- | @since 1.2.6.0
deriving newtype instance VGM.MVector VU.MVector (ModInt64 a)

-- | @since 1.2.6.0
deriving newtype instance VG.Vector VU.Vector (ModInt64 a)

-- | @since 1.2.6.0
instance VU.Unbox (ModInt64 a)