{-# LANGUAGE CPP #-}
{-# LANGUAGE UnboxedTuples #-}
module Clash.Sized.Internal.Mod where
import GHC.Exts
((==#), (<=#), geWord#, isTrue#, minusWord#, plusWord#, uncheckedShiftL#, xor#,
timesWord2#, quotRemWord2#, and#)
#if MIN_VERSION_base(4,12,0)
import GHC.Exts (addWordC#)
#endif
#if !MIN_VERSION_base(4,12,0)
import GHC.Exts (Int#, Word#, plusWord2#, word2Int#)
#endif
import GHC.Natural (Natural (..))
import GHC.Integer.GMP.Internals
(BigNat, Integer (..), bigNatToWord, compareBigNat, minusBigNat, minusBigNatWord,
plusBigNat, plusBigNatWord, sizeofBigNat#, bitBigNat, wordToBigNat2,
remBigNat, timesBigNat, timesBigNatWord, xorBigNat, wordToBigNat, andBigNat)
#if !MIN_VERSION_base(4,12,0)
import GHC.Integer.GMP.Internals (wordToInteger)
#endif
#include "MachDeps.h"
subMod :: Natural -> Natural -> Natural -> Natural
subMod (NatS# m#) (NatS# x#) (NatS# y#) =
if isTrue# (x# `geWord#` y#) then NatS# z# else NatS# (z# `plusWord#` m#)
where
z# = x# `minusWord#` y#
subMod NatS#{} _ _ = brokenInvariant
subMod (NatJ# m#) (NatS# x#) (NatS# y#) =
if isTrue# (x# `geWord#` y#)
then NatS# (x# `minusWord#` y#)
else bigNatToNat $ m# `minusBigNatWord` (y# `minusWord#` x#)
subMod (NatJ# m#) (NatS# x#) (NatJ# y#) =
bigNatToNat $ (m# `minusBigNat` y#) `plusBigNatWord` x#
subMod NatJ#{} (NatJ# x#) (NatS# y#) =
bigNatToNat $ x# `minusBigNatWord` y#
subMod (NatJ# m#) (NatJ# x#) (NatJ# y#) = case x# `compareBigNat` y# of
LT -> bigNatToNat $ (m# `minusBigNat` y#) `plusBigNat` x#
EQ -> NatS# 0##
GT -> bigNatToNat $ x# `minusBigNat` y#
addMod :: Natural -> Natural -> Natural -> Natural
addMod (NatS# m#) (NatS# x#) (NatS# y#) =
if isTrue# c# || isTrue# (z# `geWord#` m#) then NatS# (z# `minusWord#` m#) else NatS# z#
where
!(# z#, c# #) = x# `addWordC#` y#
addMod NatS#{} _ _ = brokenInvariant
addMod (NatJ# m#) (NatS# x#) (NatS# y#) =
if isTrue# c# then subIfGe (wordToBigNat2 1## z#) m# else NatS# z#
where
!(# z#, c# #) = x# `addWordC#` y#
addMod (NatJ# m#) (NatS# x#) (NatJ# y#) = subIfGe (y# `plusBigNatWord` x#) m#
addMod (NatJ# m#) (NatJ# x#) (NatS# y#) = subIfGe (x# `plusBigNatWord` y#) m#
addMod (NatJ# m#) (NatJ# x#) (NatJ# y#) = subIfGe (x# `plusBigNat` y#) m#
mulMod :: Natural -> Natural -> Natural -> Natural
mulMod (NatS# m#) (NatS# x#) (NatS# y#) = NatS# r#
where
!(# z1#, z2# #) = timesWord2# x# y#
!(# _, r# #) = quotRemWord2# z1# z2# m#
mulMod NatS#{} _ _ = brokenInvariant
mulMod (NatJ# m#) (NatS# x#) (NatS# y#) =
bigNatToNat $ wordToBigNat2 z1# z2# `remBigNat` m#
where
!(# z1#, z2# #) = timesWord2# x# y#
mulMod (NatJ# m#) (NatS# x#) (NatJ# y#) =
bigNatToNat $ (y# `timesBigNatWord` x#) `remBigNat` m#
mulMod (NatJ# m#) (NatJ# x#) (NatS# y#) =
bigNatToNat $ (x# `timesBigNatWord` y#) `remBigNat` m#
mulMod (NatJ# m#) (NatJ# x#) (NatJ# y#) =
bigNatToNat $ (x# `timesBigNat` y#) `remBigNat` m#
mulMod2 :: Natural -> Natural -> Natural -> Natural
mulMod2 (NatS# m#) (NatS# x#) (NatS# y#) = NatS# (z2# `and#` m#)
where
!(# _, z2# #) = timesWord2# x# y#
mulMod2 NatS#{} _ _ = brokenInvariant
mulMod2 (NatJ# m#) (NatS# x#) (NatS# y#) =
bigNatToNat $ wordToBigNat2 z1# z2# `andBigNat` m#
where
!(# z1#, z2# #) = timesWord2# x# y#
mulMod2 (NatJ# m#) (NatS# x#) (NatJ# y#) =
bigNatToNat $ (y# `timesBigNatWord` x#) `andBigNat` m#
mulMod2 (NatJ# m#) (NatJ# x#) (NatS# y#) =
bigNatToNat $ (x# `timesBigNatWord` y#) `andBigNat` m#
mulMod2 (NatJ# m#) (NatJ# x#) (NatJ# y#) =
bigNatToNat $ (x# `timesBigNat` y#) `andBigNat` m#
negateMod :: Natural -> Natural -> Natural
negateMod _ (NatS# 0##) = NatS# 0##
negateMod (NatS# m#) (NatS# x#) = NatS# (m# `minusWord#` x#)
negateMod NatS#{} _ = brokenInvariant
negateMod (NatJ# m#) (NatS# x#) = bigNatToNat $ m# `minusBigNatWord` x#
negateMod (NatJ# m#) (NatJ# x#) = bigNatToNat $ m# `minusBigNat` x#
complementMod
:: Integer
-> (Natural -> Natural)
complementMod (S# sz#) =
if isTrue# (sz# <=# WORD_SIZE_IN_BITS#) then
let m# = if isTrue# (sz# ==# WORD_SIZE_IN_BITS#) then
#if WORD_SIZE_IN_BITS == 64
0xFFFFFFFFFFFFFFFF##
#elif WORD_SIZE_IN_BITS == 32
0xFFFFFFFF##
#else
#error Unhandled value for WORD_SIZE_IN_BITS
#endif
else
(1## `uncheckedShiftL#` sz#) `minusWord#` 1##
go (NatS# x#) = NatS# (x# `xor#` m#)
go (NatJ# r#) = NatS# (bigNatToWord r# `xor#` m#)
in go
else
let m# = bitBigNat sz# `minusBigNatWord` 1##
go (NatS# x#) = bigNatToNat (xorBigNat (wordToBigNat x#) m#)
go (NatJ# x#) = bigNatToNat (xorBigNat x# m#)
in go
complementMod _ = error "size too large"
maskMod
:: Integer
-> (Natural -> Natural)
maskMod (S# sz#) =
if isTrue# (sz# <=# WORD_SIZE_IN_BITS#) then
if isTrue# (sz# ==# WORD_SIZE_IN_BITS#) then
let go (NatJ# x#) = NatS# (bigNatToWord x#)
go n = n
in go
else
let m# = (1## `uncheckedShiftL#` sz#) `minusWord#` 1##
go (NatS# x#) = NatS# (x# `and#` m#)
go (NatJ# x#) = NatS# (bigNatToWord x# `and#` m#)
in go
else
let m# = bitBigNat sz#
go (NatJ# x#) = bigNatToNat (remBigNat x# m#)
go x = x
in go
maskMod _ = error "size too large"
bigNatToNat :: BigNat -> Natural
bigNatToNat r# =
if isTrue# (sizeofBigNat# r# ==# 1#) then
NatS# (bigNatToWord r#)
else
NatJ# r#
subIfGe :: BigNat -> BigNat -> Natural
subIfGe z# m# = case z# `compareBigNat` m# of
LT -> NatJ# z#
EQ -> NatS# 0##
GT -> bigNatToNat $ z# `minusBigNat` m#
#if !MIN_VERSION_base(4,12,0)
addWordC# :: Word# -> Word# -> (# Word#, Int# #)
addWordC# x# y# = (# z#, word2Int# c# #)
where
!(# c#, z# #) = x# `plusWord2#` y#
naturalToInteger :: Natural -> Integer
naturalToInteger (NatS# w) = wordToInteger w
naturalToInteger (NatJ# bn) = Jp# bn
#endif
brokenInvariant :: a
brokenInvariant = error "argument is larger than modulo"