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:
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)