fixed

Pure Haskell large fixed-width integers and Montgomery arithmetic.
git clone git://git.ppad.tech/fixed.git
Log | Files | Refs | README | LICENSE

commit 4c5b8fa72b0c8044a1b78a01de68b47ff930dbac
parent bbe5cf300c2e9ad9238cab2ab243a081f8923bc9
Author: Jared Tobin <jared@jtobin.io>
Date:   Fri, 12 Dec 2025 16:29:37 +0400

lib: add 'odd' for wider and montgomery

Diffstat:
Mlib/Data/Word/Wider.hs | 21++++++++++++++++++++-
Mlib/Numeric/Montgomery/Secp256k1/Curve.hs | 20+++++++++++++++++++-
Mlib/Numeric/Montgomery/Secp256k1/Scalar.hs | 18++++++++++++++++++
3 files changed, 57 insertions(+), 2 deletions(-)

diff --git a/lib/Data/Word/Wider.hs b/lib/Data/Word/Wider.hs @@ -29,6 +29,10 @@ module Data.Word.Wider ( , gt# , cmp# + -- * Parity + , odd# + , odd + -- * Constant-time selection , select , select# @@ -79,7 +83,7 @@ import Data.Word.Limb (Limb(..)) import qualified Data.Word.Limb as L import GHC.Exts (Word(..), Int(..), Int#) import qualified GHC.Exts as Exts -import Prelude hiding (div, mod, or, and, not, quot, rem, recip) +import Prelude hiding (div, mod, or, and, not, quot, rem, recip, odd) -- utilities ------------------------------------------------------------------ @@ -728,3 +732,18 @@ sqr (Wider w) = let !(# l, h #) = sqr# w in (Wider l, Wider h) +odd# :: (# Limb, Limb, Limb, Limb #) -> C.Choice +odd# (# Limb w, _, _, _ #) = C.from_word_lsb# (Exts.and# w 1##) +{-# INLINE odd# #-} + +-- | Check if a 'Wider' is odd. +-- +-- >>> odd 1 +-- True +-- >>> odd 2 +-- False +odd + :: Wider + -> Bool +odd (Wider w) = C.decide (odd# w) + diff --git a/lib/Numeric/Montgomery/Secp256k1/Curve.hs b/lib/Numeric/Montgomery/Secp256k1/Curve.hs @@ -49,6 +49,8 @@ module Numeric.Montgomery.Secp256k1.Curve ( , inv# , sqrt , exp + , odd# + , odd ) where import Control.DeepSeq @@ -60,7 +62,7 @@ import qualified Data.Word.Wide as W import Data.Word.Wider (Wider(..)) import qualified Data.Word.Wider as WW import GHC.Exts (Word(..)) -import Prelude hiding (div, mod, or, and, not, quot, rem, recip, sqrt, exp) +import Prelude hiding (or, and, not, sqrt, exp, odd) -- montgomery arithmetic, specialized to the secp256k1 field prime modulus -- 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F @@ -998,6 +1000,8 @@ sqrt n = then Just $! rv else Nothing +-- XX want unboxed variants + -- | Exponentiation in the Montgomery domain. -- -- >>> exp 2 3 @@ -1014,3 +1018,17 @@ exp b = loop 1 b where | otherwise = r in loop nr nm ne _ -> r + +odd# :: (# Limb, Limb, Limb, Limb #) -> C.Choice +odd# = WW.odd# + +-- | Check if a 'Montgomery' value is odd. +-- +-- >>> odd 1 +-- True +-- >>> odd 2 +-- False +-- >>> Data.Word.Wider.odd (retr 3) -- parity is preserved +-- True +odd :: Montgomery -> Bool +odd (Montgomery m) = C.decide (odd# m) diff --git a/lib/Numeric/Montgomery/Secp256k1/Scalar.hs b/lib/Numeric/Montgomery/Secp256k1/Scalar.hs @@ -48,6 +48,8 @@ module Numeric.Montgomery.Secp256k1.Scalar ( , inv , inv# , exp + , odd# + , odd ) where import Control.DeepSeq @@ -936,6 +938,8 @@ inv -> Montgomery -- ^ inverse inv (Montgomery w) = Montgomery (inv# w) +-- XX want unboxed variant + -- | Exponentiation in the Montgomery domain. -- -- >>> exp 2 3 @@ -952,3 +956,17 @@ exp b = loop 1 b where | otherwise = r in loop nr nm ne _ -> r + +odd# :: (# Limb, Limb, Limb, Limb #) -> C.Choice +odd# = WW.odd# + +-- | Check if a 'Montgomery' value is odd. +-- +-- >>> odd 1 +-- True +-- >>> odd 2 +-- False +-- >>> Data.Word.Wider.odd (retr 3) -- parity is preserved +-- True +odd :: Montgomery -> Bool +odd (Montgomery m) = C.decide (odd# m)