add quadratic field extension C implementation (will be useful for vector operations?)

This commit is contained in:
Balazs Komuves 2025-10-21 13:14:07 +02:00
parent b2d8b577f7
commit 4d65a0b042
No known key found for this signature in database
GPG Key ID: F63B7AEF18435562
12 changed files with 646 additions and 174 deletions

View File

@ -16,18 +16,26 @@ We could significantly improve the speed of the Haskell implementation by bindin
for some of the critical routines: Goldilocks field and extension, hashing, fast Fourier for some of the critical routines: Goldilocks field and extension, hashing, fast Fourier
transform. transform.
The switch between the simple but intentionally naive (and very slow) Haskell
implementation and the significantly faster C bindings is controlled by by the
C preprocessor flag `-DUSE_NAIVE_HASKELL` (so the faster one is the default).
### Implementation status ### Implementation status
- [ ] cabalize
- [x] FRI prover - [x] FRI prover
- [x] FRI verifier - [x] FRI verifier
- [x] proof serialization - [x] proof serialization
- [ ] serious testing of the FRI verifier - [ ] serious testing of the FRI verifier
- [ ] full outsourcing protocol - [ ] full outsourcing protocol
- [ ] command line interface
- [x] faster Goldilocks field operations via C FFI - [x] faster Goldilocks field operations via C FFI
- [ ] quadratic field extension in C too - [x] quadratic field extension in C too (useful for the folding prover?)
- [x] faster hashing via C FFI - [x] faster hashing via C FFI
- [ ] faster NTT via C FFI - [ ] faster NTT via C FFI
- [ ] disk layout optimization
- [ ] end-to-end workflow with input/output data in files
- [ ] command line interface
- [ ] even more detailed documentation of the protocol
### References ### References

View File

@ -1,176 +1,14 @@
-- | Quadratic extension over the Goldilocks field {-# LANGUAGE CPP #-}
--
-- We use the irreducble polynomial @x^2 - 7@ to be compatible with Plonky3
module Field.Goldilocks.Extension where #ifdef USE_NAIVE_HASKELL
-------------------------------------------------------------------------------- module Field.Goldilocks.Extension ( module Field.Goldilocks.Extension.Haskell ) where
import Field.Goldilocks.Extension.Haskell
import Prelude hiding ( div ) #else
import Data.Bits module Field.Goldilocks.Extension ( module Field.Goldilocks.Extension.BindC ) where
import Data.Ratio import Field.Goldilocks.Extension.BindC
import System.Random #endif
import Foreign.Ptr
import Foreign.Storable
import Data.Binary
import Field.Goldilocks ( F )
import qualified Field.Goldilocks as Goldi
--------------------------------------------------------------------------------
type FExt = F2
data F2 = F2
{ real :: !F
, imag :: !F
}
deriving (Eq)
instance Binary F2 where
put (F2 x y) = put x >> put y
get = F2 <$> get <*> get
instance Show F2 where
show (F2 r i) = "(" ++ show r ++ " + j * " ++ show i ++ ")"
instance Num F2 where
fromInteger = inj . fromIntegral
negate = neg
(+) = add
(-) = sub
(*) = mul
abs = id
signum _ = inj 1
instance Fractional F2 where
fromRational y = fromInteger (numerator y) `div` fromInteger (denominator y)
recip = inv
(/) = div
instance Random F2 where
-- random :: RandomGen g => g -> (a, g)
random g = let (x,g' ) = random g
(y,g'') = random g'
in (F2 x y, g'')
randomR = error "randomR/F2: doesn't make any sense"
instance Storable F2 where
peek ptr = do
r <- peek (castPtr ptr)
i <- peek (castPtr ptr `plusPtr` 8)
return (F2 r i)
poke ptr (F2 r i) = do
poke (castPtr ptr) r
poke (castPtr ptr `plusPtr` 8) i
sizeOf _ = 16
alignment _ = 8
--------------------------------------------------------------------------------
zero, one, two :: F2
zero = F2 Goldi.zero Goldi.zero
one = F2 Goldi.one Goldi.zero
two = F2 Goldi.two Goldi.zero
isZero, isOne :: F2 -> Bool
isZero (F2 r i) = Goldi.isZero r && Goldi.isZero i
isOne (F2 r i) = Goldi.isOne r && Goldi.isZero i
--------------------------------------------------------------------------------
inj :: F -> F2
inj r = F2 r 0
neg :: F2 -> F2
neg (F2 r i) = F2 (negate r) (negate i)
add :: F2 -> F2 -> F2
add (F2 r1 i1) (F2 r2 i2) = F2 (r1 + r2) (i1 + i2)
sub :: F2 -> F2 -> F2
sub (F2 r1 i1) (F2 r2 i2) = F2 (r1 - r2) (i1 - i2)
scl :: F -> F2 -> F2
scl s (F2 r i) = F2 (s * r) (s * i)
sqrNaive :: F2 -> F2
sqrNaive (F2 r i) = F2 r3 i3 where
r3 = r*r + 7 * i*i
i3 = 2 * r*i
mulNaive :: F2 -> F2 -> F2
mulNaive (F2 r1 i1) (F2 r2 i2) = F2 r3 i3 where
r3 = r1 * r2 + 7 * i1 * i2
i3 = r1 * i2 + i1 * r2
-- uses Karatsuba trick to have one less multiplications
mulKaratsuba :: F2 -> F2 -> F2
mulKaratsuba (F2 r1 i1) (F2 r2 i2) = F2 r3 i3 where
u = r1*r2
w = i1*i2
v = (r1+i1)*(r2+i2)
r3 = u + 7*w
i3 = v - u - w
sqr :: F2 -> F2
sqr = sqrNaive
mul :: F2 -> F2 -> F2
mul = mulKaratsuba
--------------------------------------------------------------------------------
-- * inverse and division
-- | We can solve the equation explicitly.
--
-- > irred = x^2 + p*x + q
-- > (a*x + b) * (c*x + d) = (a*c)*x^2 + (a*d+b*c)*x + (b*d)
-- > = (a*d + b*c - a*c*p)*x + (b*d - a*c*q)
--
-- and then we want to solve
--
-- > b*d - a*c*q == 1
-- > a*d + b*c - a*c*p == 0
--
-- which has the solution:
--
-- > c = - a / (b^2 - a*b*p + a^2*q)
-- > d = (b - a*p) / (b^2 - a*b*p + a^2*q)
--
-- Remark: It seems the denominator being zero would mean that our
-- defining polynomial is not irreducible.
--
-- Note: we can optimize for the common case p=0; and also for q=1.
--
inv :: F2 -> F2
inv (F2 b a) = F2 d c where
denom = b*b - 7*a*a
c = - a / denom
d = b / denom
div :: F2 -> F2 -> F2
div u v = mul u (inv v)
--------------------------------------------------------------------------------
pow_ :: F2 -> Int -> F2
pow_ x e = pow x (fromIntegral e)
pow :: F2 -> Integer -> F2
pow x e
| e == 0 = 1
| e < 0 = pow (inv x) (negate e)
| otherwise = go 1 x e
where
go !acc _ 0 = acc
go !acc !s !expo = case expo .&. 1 of
0 -> go acc (sqr s) (shiftR expo 1)
_ -> go (acc*s) (sqr s) (shiftR expo 1)
--------------------------------------------------------------------------------

View File

@ -0,0 +1,193 @@
-- | Bindings to a C implementation of the quadratic extension over the Goldilocks prime field
--
-- It's probably not significantly (if at all) faster than the naive Haskell combined
-- with the fast Goldilocks base field operations, but the C versions should be useful
-- for the vector operations, and this way we can test them easily.
{-# LANGUAGE ForeignFunctionInterface, BangPatterns, NumericUnderscores #-}
module Field.Goldilocks.Extension.BindC where
--------------------------------------------------------------------------------
import Prelude hiding ( div )
import qualified Prelude
import Data.Bits
import Data.Word
import Data.Ratio
import Foreign.C
import Foreign.Ptr
import Foreign.Storable
import Foreign.Marshal
import System.Random
import System.IO.Unsafe
import Data.Binary
import Data.Binary.Get ( getWord64le )
import Data.Binary.Put ( putWord64le )
import Text.Printf
import Field.Goldilocks ( F , Goldilocks(..) )
import qualified Field.Goldilocks as Goldi
import Data.Flat
--------------------------------------------------------------------------------
type FExt = F2
data F2 = F2
{ real :: !F
, imag :: !F
}
deriving (Eq)
--------------------------------------------------------------------------------
instance Binary F2 where
put (F2 x y) = put x >> put y
get = F2 <$> get <*> get
instance Storable F2 where
peek ptr = do
r <- peek (castPtr ptr)
i <- peek (castPtr ptr `plusPtr` 8)
return (F2 r i)
poke ptr (F2 r i) = do
poke (castPtr ptr) r
poke (castPtr ptr `plusPtr` 8) i
sizeOf _ = 16
alignment _ = 8
instance Flat F2 where
sizeInBytes _ = 16
sizeInQWords _ = 2
withFlat (F2 x y) action = allocaBytesAligned 16 8 $ \ptr -> do
poke (castPtr ptr ) x
poke (castPtr ptr `plusPtr` 8) y
action ptr
makeFlat ptr = do
x <- peek (castPtr ptr )
y <- peek (castPtr ptr `plusPtr` 8)
return (F2 x y)
--------------------------------------------------------------------------------
instance Show F2 where
show (F2 r i) = "(" ++ show r ++ " + j * " ++ show i ++ ")"
instance Num F2 where
fromInteger = inj . fromIntegral
negate = neg
(+) = add
(-) = sub
(*) = mul
abs = id
signum _ = inj 1
instance Fractional F2 where
fromRational y = fromInteger (numerator y) `div` fromInteger (denominator y)
recip = inv
(/) = div
instance Random F2 where
-- random :: RandomGen g => g -> (a, g)
random g = let (x,g' ) = random g
(y,g'') = random g'
in (F2 x y, g'')
randomR = error "randomR/F2: doesn't make any sense"
--------------------------------------------------------------------------------
zero, one, two :: F2
zero = F2 Goldi.zero Goldi.zero
one = F2 Goldi.one Goldi.zero
two = F2 Goldi.two Goldi.zero
isZero, isOne :: F2 -> Bool
isZero (F2 r i) = Goldi.isZero r && Goldi.isZero i
isOne (F2 r i) = Goldi.isOne r && Goldi.isZero i
--------------------------------------------------------------------------------
{-# NOINLINE unaryOpIO #-}
unaryOpIO :: (Ptr Word64 -> Ptr Word64 -> IO ()) -> F2 -> IO F2
unaryOpIO c_action x = allocaBytesAligned 32 8 $ \ptr1 -> do
let ptr2 = plusPtr ptr1 16
poke (castPtr ptr1) x
c_action ptr1 ptr2
peek (castPtr ptr2)
{-# NOINLINE binaryOpIO #-}
binaryOpIO :: (Ptr Word64 -> Ptr Word64 -> Ptr Word64 -> IO ()) -> F2 -> F2 -> IO F2
binaryOpIO c_action x y = allocaBytesAligned 48 8 $ \ptr1 -> do
let ptr2 = plusPtr ptr1 16
let ptr3 = plusPtr ptr1 32
poke (castPtr ptr1) x
poke (castPtr ptr2) y
c_action ptr1 ptr2 ptr3
peek (castPtr ptr3)
--------------------------------------------------------------------------------
inj :: F -> F2
inj r = F2 r 0
neg, sqr, inv :: F2 -> F2
neg x = unsafePerformIO (unaryOpIO c_goldilocks_ext_neg x)
sqr x = unsafePerformIO (unaryOpIO c_goldilocks_ext_sqr x)
inv x = unsafePerformIO (unaryOpIO c_goldilocks_ext_inv x)
add, sub, mul, div :: F2 -> F2 -> F2
add x y = unsafePerformIO (binaryOpIO c_goldilocks_ext_add x y)
sub x y = unsafePerformIO (binaryOpIO c_goldilocks_ext_sub x y)
mul x y = unsafePerformIO (binaryOpIO c_goldilocks_ext_mul x y)
div x y = unsafePerformIO (binaryOpIO c_goldilocks_ext_div x y)
{-# NOINLINE sclIO #-}
sclIO :: F -> F2 -> IO F2
sclIO (MkGoldilocks s) x = allocaBytesAligned 32 8 $ \ptr1 -> do
let ptr2 = plusPtr ptr1 16
poke (castPtr ptr1) x
c_goldilocks_ext_scl s ptr1 ptr2
peek (castPtr ptr2)
{-# NOINLINE powIO #-}
powIO :: F2 -> Int -> IO F2
powIO base expo = allocaBytesAligned 32 8 $ \ptr1 -> do
let ptr2 = plusPtr ptr1 16
poke (castPtr ptr1) base
c_goldilocks_ext_pow ptr1 (fromIntegral expo :: CInt) ptr2
peek (castPtr ptr2)
scl :: F -> F2 -> F2
scl s x = unsafePerformIO (sclIO s x)
pow_ :: F2 -> Int -> F2
pow_ b e = unsafePerformIO (powIO b e)
-- | NOTE: this is technically incorrect (it only works for small exponents),
-- but we don't really care
pow :: F2 -> Integer -> F2
pow b e = pow_ b (fromInteger e)
--------------------------------------------------------------------------------
foreign import ccall unsafe "goldilocks_ext_neg" c_goldilocks_ext_neg :: Ptr Word64 -> Ptr Word64 -> IO ()
foreign import ccall unsafe "goldilocks_ext_add" c_goldilocks_ext_add :: Ptr Word64 -> Ptr Word64 -> Ptr Word64 -> IO ()
foreign import ccall unsafe "goldilocks_ext_sub" c_goldilocks_ext_sub :: Ptr Word64 -> Ptr Word64 -> Ptr Word64 -> IO ()
foreign import ccall unsafe "goldilocks_ext_scl" c_goldilocks_ext_scl :: Word64 -> Ptr Word64 -> Ptr Word64 -> IO ()
foreign import ccall unsafe "goldilocks_ext_sqr" c_goldilocks_ext_sqr :: Ptr Word64 -> Ptr Word64 -> IO ()
foreign import ccall unsafe "goldilocks_ext_mul" c_goldilocks_ext_mul :: Ptr Word64 -> Ptr Word64 -> Ptr Word64 -> IO ()
foreign import ccall unsafe "goldilocks_ext_inv" c_goldilocks_ext_inv :: Ptr Word64 -> Ptr Word64 -> IO ()
foreign import ccall unsafe "goldilocks_ext_div" c_goldilocks_ext_div :: Ptr Word64 -> Ptr Word64 -> Ptr Word64 -> IO ()
foreign import ccall unsafe "goldilocks_ext_pow" c_goldilocks_ext_pow :: Ptr Word64 -> CInt -> Ptr Word64 -> IO ()
--------------------------------------------------------------------------------

View File

@ -0,0 +1,197 @@
-- | Quadratic extension over the Goldilocks field
--
-- We use the irreducble polynomial @x^2 - 7@ to be compatible with Plonky3
module Field.Goldilocks.Extension.Haskell where
--------------------------------------------------------------------------------
import Prelude hiding ( div )
import Data.Bits
import Data.Ratio
import System.Random
import Foreign.Ptr
import Foreign.Storable
import Foreign.Marshal
import Data.Binary
import Data.Flat
import Field.Goldilocks ( F )
import qualified Field.Goldilocks as Goldi
--------------------------------------------------------------------------------
type FExt = F2
data F2 = F2
{ real :: !F
, imag :: !F
}
deriving (Eq)
--------------------------------------------------------------------------------
instance Binary F2 where
put (F2 x y) = put x >> put y
get = F2 <$> get <*> get
instance Storable F2 where
peek ptr = do
r <- peek (castPtr ptr)
i <- peek (castPtr ptr `plusPtr` 8)
return (F2 r i)
poke ptr (F2 r i) = do
poke (castPtr ptr) r
poke (castPtr ptr `plusPtr` 8) i
sizeOf _ = 16
alignment _ = 8
instance Flat F2 where
sizeInBytes _ = 16
sizeInQWords _ = 2
withFlat (F2 x y) action = allocaBytesAligned 16 8 $ \ptr -> do
poke (castPtr ptr ) x
poke (castPtr ptr `plusPtr` 8) y
action ptr
makeFlat ptr = do
x <- peek (castPtr ptr )
y <- peek (castPtr ptr `plusPtr` 8)
return (F2 x y)
--------------------------------------------------------------------------------
instance Show F2 where
show (F2 r i) = "(" ++ show r ++ " + j * " ++ show i ++ ")"
instance Num F2 where
fromInteger = inj . fromIntegral
negate = neg
(+) = add
(-) = sub
(*) = mul
abs = id
signum _ = inj 1
instance Fractional F2 where
fromRational y = fromInteger (numerator y) `div` fromInteger (denominator y)
recip = inv
(/) = div
instance Random F2 where
-- random :: RandomGen g => g -> (a, g)
random g = let (x,g' ) = random g
(y,g'') = random g'
in (F2 x y, g'')
randomR = error "randomR/F2: doesn't make any sense"
--------------------------------------------------------------------------------
zero, one, two :: F2
zero = F2 Goldi.zero Goldi.zero
one = F2 Goldi.one Goldi.zero
two = F2 Goldi.two Goldi.zero
isZero, isOne :: F2 -> Bool
isZero (F2 r i) = Goldi.isZero r && Goldi.isZero i
isOne (F2 r i) = Goldi.isOne r && Goldi.isZero i
--------------------------------------------------------------------------------
inj :: F -> F2
inj r = F2 r 0
neg :: F2 -> F2
neg (F2 r i) = F2 (negate r) (negate i)
add :: F2 -> F2 -> F2
add (F2 r1 i1) (F2 r2 i2) = F2 (r1 + r2) (i1 + i2)
sub :: F2 -> F2 -> F2
sub (F2 r1 i1) (F2 r2 i2) = F2 (r1 - r2) (i1 - i2)
scl :: F -> F2 -> F2
scl s (F2 r i) = F2 (s * r) (s * i)
sqrNaive :: F2 -> F2
sqrNaive (F2 r i) = F2 r3 i3 where
r3 = r*r + 7 * i*i
i3 = 2 * r*i
mulNaive :: F2 -> F2 -> F2
mulNaive (F2 r1 i1) (F2 r2 i2) = F2 r3 i3 where
r3 = r1 * r2 + 7 * i1 * i2
i3 = r1 * i2 + i1 * r2
-- uses Karatsuba trick to have one less multiplications
mulKaratsuba :: F2 -> F2 -> F2
mulKaratsuba (F2 r1 i1) (F2 r2 i2) = F2 r3 i3 where
u = r1*r2
w = i1*i2
v = (r1+i1)*(r2+i2)
r3 = u + 7*w
i3 = v - u - w
sqr :: F2 -> F2
sqr = sqrNaive
mul :: F2 -> F2 -> F2
mul = mulKaratsuba
--------------------------------------------------------------------------------
-- * inverse and division
-- | We can solve the equation explicitly.
--
-- > irred = x^2 + p*x + q
-- > (a*x + b) * (c*x + d) = (a*c)*x^2 + (a*d+b*c)*x + (b*d)
-- > = (a*d + b*c - a*c*p)*x + (b*d - a*c*q)
--
-- and then we want to solve
--
-- > b*d - a*c*q == 1
-- > a*d + b*c - a*c*p == 0
--
-- which has the solution:
--
-- > c = - a / (b^2 - a*b*p + a^2*q)
-- > d = (b - a*p) / (b^2 - a*b*p + a^2*q)
--
-- Remark: It seems the denominator being zero would mean that our
-- defining polynomial is not irreducible.
--
-- Note: we can optimize for the common case p=0; and also for q=1.
--
inv :: F2 -> F2
inv (F2 b a) = F2 d c where
denom = b*b - 7*a*a
c = - a / denom
d = b / denom
div :: F2 -> F2 -> F2
div u v = mul u (inv v)
--------------------------------------------------------------------------------
pow_ :: F2 -> Int -> F2
pow_ x e = pow x (fromIntegral e)
pow :: F2 -> Integer -> F2
pow x e
| e == 0 = 1
| e < 0 = pow (inv x) (negate e)
| otherwise = go 1 x e
where
go !acc _ 0 = acc
go !acc !s !expo = case expo .&. 1 of
0 -> go acc (sqr s) (shiftR expo 1)
_ -> go (acc*s) (sqr s) (shiftR expo 1)
--------------------------------------------------------------------------------

View File

@ -16,6 +16,7 @@ import Data.Ratio
import Foreign.C import Foreign.C
import Foreign.Ptr import Foreign.Ptr
import Foreign.Storable import Foreign.Storable
import Foreign.Marshal
import System.Random import System.Random
@ -25,6 +26,8 @@ import Data.Binary.Put ( putWord64le )
import Text.Printf import Text.Printf
import Data.Flat
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
type F = Goldilocks type F = Goldilocks
@ -51,6 +54,12 @@ instance Storable F where
sizeOf _ = 8 sizeOf _ = 8
alignment _ = 8 alignment _ = 8
instance Flat Goldilocks where
sizeInBytes _ = 8
sizeInQWords _ = 1
withFlat (MkGoldilocks x) action = alloca $ \ptr -> poke ptr x >> action ptr
makeFlat ptr = MkGoldilocks <$> peek ptr
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
newtype Goldilocks newtype Goldilocks

View File

@ -13,6 +13,10 @@ import Data.Bits
import Data.Word import Data.Word
import Data.Ratio import Data.Ratio
import Foreign.Ptr
import Foreign.Storable
import Foreign.Marshal
import System.Random import System.Random
import Data.Binary import Data.Binary
@ -21,6 +25,8 @@ import Data.Binary.Put ( putWord64le )
import Text.Printf import Text.Printf
import Data.Flat
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
type F = Goldilocks type F = Goldilocks
@ -34,10 +40,24 @@ toF = mkGoldilocks . fromIntegral
intToF :: Int -> F intToF :: Int -> F
intToF = mkGoldilocks . fromIntegral intToF = mkGoldilocks . fromIntegral
--------------------------------------------------------------------------------
instance Binary F where instance Binary F where
put x = putWord64le (fromF x) put x = putWord64le (fromF x)
get = toF <$> getWord64le get = toF <$> getWord64le
instance Storable F where
peek ptr = (MkGoldilocks . fromIntegral) <$> peek (castPtr ptr :: Ptr Word64)
poke ptr (MkGoldilocks x) = poke (castPtr ptr :: Ptr Word64) (fromInteger x :: Word64)
sizeOf _ = 8
alignment _ = 8
instance Flat Goldilocks where
sizeInBytes _ = 8
sizeInQWords _ = 1
withFlat (MkGoldilocks x) action = alloca $ \ptr -> poke ptr (fromInteger x :: Word64) >> action ptr
makeFlat ptr = (MkGoldilocks . fromIntegral) <$> peek ptr
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
newtype Goldilocks newtype Goldilocks

View File

@ -1,4 +1,7 @@
#!/bin/bash #!/bin/bash
gcc -c -O2 goldilocks.c gcc -c -O2 goldilocks.c
gcc -c -O2 goldilocks_ext.c
gcc -c -O2 monolith.c gcc -c -O2 monolith.c
gcc -c -O2 ntt.c

View File

@ -1,4 +1,6 @@
// the "Goldilocks" prime field of size `p = 2^64 - 2^32 + 1`
#include <stdint.h> #include <stdint.h>
#include <stdio.h> // for testing only #include <stdio.h> // for testing only
#include <assert.h> #include <assert.h>
@ -126,6 +128,22 @@ uint64_t goldilocks_mul_small(uint64_t x, uint32_t y) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
uint64_t goldilocks_mul_by_2(uint64_t x) {
return goldilocks_add( x , x );
}
uint64_t goldilocks_a_plus_7b(uint64_t a, uint64_t b) {
__uint128_t z = (__uint128_t)a + 7 * (__uint128_t)b ;
return goldilocks_rdc_small( z );
}
uint64_t goldilocks_a_minus_7b(uint64_t a, uint64_t b) {
uint64_t b7 = goldilocks_rdc_small ( 7 * (__uint128_t)b ) ;
return goldilocks_sub( a , b7 );
}
//------------------------------------------------------------------------------
uint64_t goldilocks_euclid(uint64_t x0, uint64_t y0, uint64_t u0, uint64_t v0) { uint64_t goldilocks_euclid(uint64_t x0, uint64_t y0, uint64_t u0, uint64_t v0) {
uint64_t x = x0; uint64_t x = x0;
@ -178,6 +196,15 @@ uint64_t goldilocks_inv(uint64_t a) {
return goldilocks_div(1, a); return goldilocks_div(1, a);
} }
uint64_t goldilocks_div_by_2(uint64_t a) {
if (a & 1) {
return ((a >> 1) + GOLDILOCKS_HALFPRIME_PLUS1);
}
else {
return (a >> 1);
}
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
uint64_t goldilocks_pow(uint64_t base, int expo) { uint64_t goldilocks_pow(uint64_t base, int expo) {
@ -193,7 +220,7 @@ uint64_t goldilocks_pow(uint64_t base, int expo) {
acc = goldilocks_mul( acc, sq ); acc = goldilocks_mul( acc, sq );
} }
if (e > 0) { if (e > 0) {
sq = goldilocks_mul( sq , sq ); sq = goldilocks_sqr( sq );
e = e >> 1; e = e >> 1;
} }
} }

View File

@ -1,4 +1,6 @@
// the "Goldilocks" prime field of size `p = 2^64 - 2^32 + 1`
#include <stdint.h> #include <stdint.h>
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -19,6 +21,11 @@ uint64_t goldilocks_inv(uint64_t a);
uint64_t goldilocks_div(uint64_t a, uint64_t b); uint64_t goldilocks_div(uint64_t a, uint64_t b);
uint64_t goldilocks_pow(uint64_t b, int e); uint64_t goldilocks_pow(uint64_t b, int e);
uint64_t goldilocks_mul_by_2 (uint64_t x);
uint64_t goldilocks_div_by_2 (uint64_t a);
uint64_t goldilocks_a_plus_7b (uint64_t a, uint64_t b);
uint64_t goldilocks_a_minus_7b(uint64_t a, uint64_t b);
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
uint64_t goldilocks_rdc (__uint128_t x); uint64_t goldilocks_rdc (__uint128_t x);

View File

@ -0,0 +1,147 @@
// quadratic field extension F[x] = F(x) / (x^2 - 7) over the Goldilocks field
#include "goldilocks.h"
#include "goldilocks_ext.h"
//------------------------------------------------------------------------------
void goldilocks_ext_inj( uint64_t input , uint64_t *out ) {
out[0] = input;
out[1] = 0;
}
void goldilocks_ext_set_one( uint64_t *out ) {
out[0] = 1;
out[1] = 0;
}
//------------------------------------------------------------------------------
void goldilocks_ext_neg(const uint64_t *x , uint64_t *out) {
out[0] = goldilocks_neg( x[0] );
out[1] = goldilocks_neg( x[1] );
}
void goldilocks_ext_add(const uint64_t *x , const uint64_t *y , uint64_t *out) {
out[0] = goldilocks_add( x[0] , y[0] );
out[1] = goldilocks_add( x[1] , y[1] );
}
void goldilocks_ext_sub(const uint64_t *x , const uint64_t *y , uint64_t *out) {
out[0] = goldilocks_sub( x[0] , y[0] );
out[1] = goldilocks_sub( x[1] , y[1] );
}
void goldilocks_ext_scl(uint64_t s , const uint64_t *x , uint64_t *out) {
out[0] = goldilocks_mul( s , x[0] );
out[1] = goldilocks_mul( s , x[1] );
}
//------------------------------------------------------------------------------
/*
sqrNaive :: F2 -> F2
sqrNaive (F2 r i) = F2 r3 i3 where
r3 = r*r + 7 * i*i
i3 = 2 * r*i
-- uses Karatsuba trick to have one less multiplications
mulKaratsuba :: F2 -> F2 -> F2
mulKaratsuba (F2 r1 i1) (F2 r2 i2) = F2 r3 i3 where
u = r1*r2
w = i1*i2
v = (r1+i1)*(r2+i2)
r3 = u + 7*w
i3 = v - u - w
*/
void goldilocks_ext_sqr(const uint64_t *x , uint64_t *out) {
uint64_t u = goldilocks_a_plus_7b( goldilocks_sqr(x[0]) , goldilocks_sqr( x[1] )) ;
uint64_t v = goldilocks_mul_by_2( goldilocks_mul( x[0] , x[1] ) );
out[0] = u;
out[1] = v; // because we want inplace to work too
}
// uses Karatsuba trick to have one less multiplications
void goldilocks_ext_mul(const uint64_t *x , const uint64_t *y , uint64_t *out) {
uint64_t u = goldilocks_mul( x[0] , y[0] );
uint64_t w = goldilocks_mul( x[1] , y[1] );
uint64_t v = goldilocks_mul( goldilocks_add( x[0] , x[1] ) , goldilocks_add( y[0] , y[1] ) );
out[0] = goldilocks_a_plus_7b( u , w );
out[1] = goldilocks_sub( v , goldilocks_add( u , w ) );
}
//------------------------------------------------------------------------------
// We can solve the equation explicitly.
//
// > irred = x^2 + p*x + q
// > (a*x + b) * (c*x + d) = (a*c)*x^2 + (a*d+b*c)*x + (b*d)
// > = (a*d + b*c - a*c*p)*x + (b*d - a*c*q)
//
// and then we want to solve
//
// > b*d - a*c*q == 1
// > a*d + b*c - a*c*p == 0
//
// which has the solution:
//
// > c = - a / (b^2 - a*b*p + a^2*q)
// > d = (b - a*p) / (b^2 - a*b*p + a^2*q)
//
// Remark: It seems the denominator being zero would mean that our
// defining polynomial is not irreducible.
//
// Note: we can optimize for the common case p=0; and also for q=1.
//
void goldilocks_ext_inv(const uint64_t *ptr , uint64_t *out) {
uint64_t b = ptr[0];
uint64_t a = ptr[1]; // (a*x + b)
uint64_t denom = goldilocks_a_minus_7b( goldilocks_sqr(b) , goldilocks_sqr(a) );
uint64_t denom_inv = goldilocks_inv( denom );
out[0] = goldilocks_mul( b , denom_inv ) ;
out[1] = goldilocks_neg( goldilocks_mul( a , denom_inv ) );
}
void goldilocks_ext_div(const uint64_t *x , const uint64_t *y , uint64_t *out) {
uint64_t invy[2];
goldilocks_ext_inv( y , invy );
goldilocks_ext_mul( x , invy , out );
}
//------------------------------------------------------------------------------
void goldilocks_ext_pow(const uint64_t *base , int expo , uint64_t *out) {
if (expo == 0) {
goldilocks_ext_set_one(out);
return;
}
if (expo < 0) {
uint64_t base_inv[2];
goldilocks_ext_inv( base , base_inv );
goldilocks_ext_pow( base_inv , -expo , out );
return;
}
int e = expo;
uint64_t sq [2] ; sq[0] = base[0] ; sq[1] = base[1] ;
uint64_t acc[2] ; goldilocks_ext_set_one( acc );
while (e != 0) {
if ((e & 1) != 0) {
goldilocks_ext_mul( acc , sq , acc );
}
if (e > 0) {
goldilocks_ext_sqr( sq , sq );
e = e >> 1;
}
}
out[0] = acc[0];
out[1] = acc[1];
}
//------------------------------------------------------------------------------

View File

@ -0,0 +1,23 @@
// quadratic field extension F[x] = F(x) / (x^2 - 7) over the Goldilocks field
#include <stdint.h>
#include "goldilocks.h"
//------------------------------------------------------------------------------
void goldilocks_ext_inj( uint64_t input , uint64_t *out );
void goldilocks_ext_set_one( uint64_t *out );
void goldilocks_ext_neg(const uint64_t *x , uint64_t *out);
void goldilocks_ext_add(const uint64_t *x , const uint64_t *y , uint64_t *out);
void goldilocks_ext_sub(const uint64_t *x , const uint64_t *y , uint64_t *out);
void goldilocks_ext_scl( uint64_t s , const uint64_t *x , uint64_t *out);
void goldilocks_ext_sqr(const uint64_t *x , uint64_t *out);
void goldilocks_ext_mul(const uint64_t *x , const uint64_t *y , uint64_t *out);
void goldilocks_ext_inv(const uint64_t *x , uint64_t *out);
void goldilocks_ext_div(const uint64_t *x , const uint64_t *y , uint64_t *out);
void goldilocks_ext_pow(const uint64_t *b , int e , uint64_t *out);
//------------------------------------------------------------------------------

View File

@ -1,3 +1,3 @@
#!/bin/bash #!/bin/bash
ghci testMain.hs cbits/goldilocks.o cbits/monolith.o ghci testMain.hs cbits/goldilocks.o cbits/goldilocks_ext.o cbits/monolith.o cbits/ntt.o