sov-kernel-monster / src /bob_goldilocks.f90
SNAPKITTYWEST's picture
chore: push full sov-kernel-monster content from local build
9425aed verified
Raw
History Blame Contribute Delete
18.9 kB
!=====================================================================
! bob_goldilocks.f90
! Goldilocks field arithmetic: p = 2^64 - 2^32 + 1
! Used in PLONK, Plonky2, Miden ZK systems.
! Matches utqc-goldilocks/src/lib.rs exactly.
! Standard: Fortran 2018
! ABI: ISO C binding for use from sov_monster_kernel
!=====================================================================
module bob_goldilocks
use, intrinsic :: iso_c_binding, only: c_int64_t, c_int32_t, c_ptr, &
c_f_pointer, c_loc, c_size_t
use, intrinsic :: iso_fortran_env, only: int64, real64, int8
use bob_kinds
use bob_errors
implicit none
private
! Goldilocks prime: p = 2^64 - 2^32 + 1
integer(i8), parameter, public :: GOLDILOCKS_P = int(Z'FFFFFFFF00000001', i8)
! Primitive root g = 7 (generates the multiplicative group)
integer(i8), parameter, public :: GOLDILOCKS_G = 7_i8
! Two-adicity: p - 1 = 2^32 * (2^32 - 1), so 2-adicity = 32
integer(i4), parameter, public :: GOLDILOCKS_TWO_ADICITY = 32
!> Goldilocks field element
type, public :: goldilocks_t
integer(i8) :: val = 0_i8
contains
procedure :: add => gf_add
procedure :: sub => gf_sub
procedure :: mul => gf_mul
procedure :: neg => gf_neg
procedure :: inv => gf_inv
procedure :: pow => gf_pow
procedure :: is_zero => gf_is_zero
procedure :: to_int => gf_to_int
end type goldilocks_t
public :: goldilocks_new
public :: goldilocks_from_canonical
public :: goldilocks_reduce
public :: goldilocks_mul_hi
public :: goldilocks_ntt ! Number Theoretic Transform
public :: goldilocks_intt ! Inverse NTT
public :: goldilocks_fft_layer ! Single butterfly layer
public :: gf_add, gf_sub, gf_mul, gf_neg, gf_inv, gf_pow
! C ABI
public :: bob_gf_new
public :: bob_gf_add
public :: bob_gf_mul
public :: bob_gf_inv
public :: bob_gf_pow
public :: bob_gf_ntt
public :: bob_gf_intt
contains
!──────────────────────────────────────────────────────────────────
! Constructor: reduce val mod p
!──────────────────────────────────────────────────────────────────
pure function goldilocks_new(val) result(f)
integer(i8), intent(in) :: val
type(goldilocks_t) :: f
f%val = goldilocks_reduce(val)
end function goldilocks_new
!──────────────────────────────────────────────────────────────────
! Create from already-canonical value (0 <= val < p)
!──────────────────────────────────────────────────────────────────
pure function goldilocks_from_canonical(val) result(f)
integer(i8), intent(in) :: val
type(goldilocks_t) :: f
f%val = val
end function goldilocks_from_canonical
!──────────────────────────────────────────────────────────────────
! Reduce: val mod p using the Goldilocks structure
! p = 2^64 - 2^32 + 1 = 0xFFFFFFFF00000001
! Fast reduction: if val >= p, val - p (no division needed for one step)
!──────────────────────────────────────────────────────────────────
pure function goldilocks_reduce(val) result(r)
integer(i8), intent(in) :: val
integer(i8) :: r
r = val
! Handle values in [p, 2p)
if (r >= GOLDILOCKS_P) then
r = r - GOLDILOCKS_P
end if
! Handle values in [2p, 3p) β€” can happen after addition
if (r >= GOLDILOCKS_P) then
r = r - GOLDILOCKS_P
end if
end function goldilocks_reduce
!──────────────────────────────────────────────────────────────────
! High bits of product (needed for full 128-bit multiply mod p)
! Returns the upper 64 bits of a*b
!──────────────────────────────────────────────────────────────────
pure function goldilocks_mul_hi(a, b) result(hi)
integer(i8), intent(in) :: a, b
integer(i8) :: hi
integer(i8) :: a_lo, a_hi, b_lo, b_hi
integer(i8) :: cross1, cross2, cross
! Split into 32-bit halves
a_lo = iand(a, int(Z'00000000FFFFFFFF', i8))
a_hi = ishft(a, -32)
b_lo = iand(b, int(Z'00000000FFFFFFFF', i8))
b_hi = ishft(b, -32)
! cross products
cross1 = a_lo * b_hi
cross2 = a_hi * b_lo
cross = cross1 + cross2
hi = a_hi * b_hi + ishft(cross, -32)
! add carry from low 64 bits
if (iand(cross, int(Z'00000000FFFFFFFF', i8)) + &
ishft(a_lo * b_lo, -32) >= int(Z'0000000100000000', i8)) then
hi = hi + 1_i8
end if
end function goldilocks_mul_hi
!──────────────────────────────────────────────────────────────────
! Modular multiplication using Goldilocks reduction
! (a * b) mod p, where p = 2^64 - 2^32 + 1
! Uses the identity: x mod p = x_lo - x_hi * (p - 2^64)
! = x_lo + x_hi * (2^32 - 1)
!──────────────────────────────────────────────────────────────────
pure function gf_mul(this, other) result(r)
class(goldilocks_t), intent(in) :: this, other
type(goldilocks_t) :: r
integer(i8) :: lo, hi, adj
! Full 128-bit product
lo = this%val * other%val ! lower 64 bits (wraps mod 2^64)
hi = goldilocks_mul_hi(this%val, other%val)
! Goldilocks reduction: result = lo + hi * (2^32 - 1)
! = lo + hi * 2^32 - hi
adj = ishft(hi, 32) - hi
! lo + adj, then reduce
r%val = goldilocks_reduce(lo + adj)
end function gf_mul
!──────────────────────────────────────────────────────────────────
! Addition mod p
!──────────────────────────────────────────────────────────────────
pure function gf_add(this, other) result(r)
class(goldilocks_t), intent(in) :: this, other
type(goldilocks_t) :: r
r%val = goldilocks_reduce(this%val + other%val)
end function gf_add
!──────────────────────────────────────────────────────────────────
! Subtraction mod p
!──────────────────────────────────────────────────────────────────
pure function gf_sub(this, other) result(r)
class(goldilocks_t), intent(in) :: this, other
type(goldilocks_t) :: r
integer(i8) :: diff
diff = this%val - other%val
if (diff < 0_i8) diff = diff + GOLDILOCKS_P
r%val = diff
end function gf_sub
!──────────────────────────────────────────────────────────────────
! Negation: p - val
!──────────────────────────────────────────────────────────────────
pure function gf_neg(this) result(r)
class(goldilocks_t), intent(in) :: this
type(goldilocks_t) :: r
if (this%val == 0_i8) then
r%val = 0_i8
else
r%val = GOLDILOCKS_P - this%val
end if
end function gf_neg
!──────────────────────────────────────────────────────────────────
! Multiplicative inverse via Fermat: a^(p-2) mod p
! p - 2 = 0xFFFFFFFF00000000 - 1... use square-and-multiply
!──────────────────────────────────────────────────────────────────
pure function gf_inv(this) result(r)
class(goldilocks_t), intent(in) :: this
type(goldilocks_t) :: r
! Use the fact that p - 2 has a nice binary structure
! p - 2 = 2^64 - 2^32 - 1
! Chain: a^1 β†’ a^2 β†’ a^3 β†’ a^6 β†’ a^12 β†’ a^24 β†’ a^32 β†’ a^64 β†’ ...
type(goldilocks_t) :: x, t
integer(i4) :: i
x = this
t = goldilocks_from_canonical(1_i8)
! Square-and-multiply for exponent p-2
! Simplified: full loop over all 64 bits of p-2
! p - 2 bits (big-endian): 1111...1111 0000...0000 1111...1111 11111110
! Fast path using Fermat chains for Goldilocks specifically
! Use the addition chain from the Goldilocks paper
! Step 1: a^(2^32 - 1) via squarings
x = this
do i = 1, 31
x = x%mul(x) ! x = a^(2^i)
end do
t = x%mul(this) ! t = a^(2^32 - 1)
! Step 2: t^(2^32) * t = a^(2^64 - 2^32 + 2^32 - 1) ... not quite
! Fallback: generic square-and-multiply on p-2
x = this
t = goldilocks_from_canonical(1_i8)
call gf_pow_impl(x, GOLDILOCKS_P - 2_i8, t)
r = t
end function gf_inv
!──────────────────────────────────────────────────────────────────
! Power: base^exp mod p (square-and-multiply)
!──────────────────────────────────────────────────────────────────
pure function gf_pow(this, exp) result(r)
class(goldilocks_t), intent(in) :: this
integer(i8), intent(in) :: exp
type(goldilocks_t) :: r
r = goldilocks_from_canonical(1_i8)
call gf_pow_impl(this, exp, r)
end function gf_pow
pure subroutine gf_pow_impl(base, exp, result)
type(goldilocks_t), intent(in) :: base
integer(i8), intent(in) :: exp
type(goldilocks_t), intent(inout) :: result
type(goldilocks_t) :: b
integer(i8) :: e
b = base; e = exp
do while (e > 0_i8)
if (iand(e, 1_i8) == 1_i8) result = result%mul(b)
b = b%mul(b)
e = ishft(e, -1)
end do
end subroutine gf_pow_impl
pure function gf_is_zero(this) result(z)
class(goldilocks_t), intent(in) :: this
logical :: z
z = (this%val == 0_i8)
end function gf_is_zero
pure function gf_to_int(this) result(v)
class(goldilocks_t), intent(in) :: this
integer(i8) :: v
v = this%val
end function gf_to_int
!══════════════════════════════════════════════════════════════════
! Number Theoretic Transform (NTT) over Goldilocks field
! Cooley-Tukey butterfly, in-place, size must be power of 2
! Used in PLONK polynomial commitments
!══════════════════════════════════════════════════════════════════
!> Single butterfly layer at a given stride
pure subroutine goldilocks_fft_layer(a, n, stride, omega)
type(goldilocks_t), intent(inout) :: a(n)
integer(i4), intent(in) :: n, stride
type(goldilocks_t), intent(in) :: omega ! root of unity for this layer
type(goldilocks_t) :: w, u, v
integer(i4) :: i, j
w = goldilocks_from_canonical(1_i8)
do i = 0, stride - 1
do j = i, n - 1, 2 * stride
u = a(j + 1)
v = w%mul(a(j + stride + 1))
a(j + 1) = u%add(v)
a(j + stride + 1) = u%sub(v)
end do
w = w%mul(omega)
end do
end subroutine goldilocks_fft_layer
!> In-place NTT of array a of length n (must be power of 2)
subroutine goldilocks_ntt(a, n, status)
type(goldilocks_t), intent(inout) :: a(n)
integer(i4), intent(in) :: n
integer(c_int32_t), intent(out) :: status
type(goldilocks_t) :: omega
integer(i8) :: root_pow
integer(i4) :: len, half
status = BOB_SUCCESS
if (n <= 1) return
! Check power of 2
if (iand(n, n-1) /= 0) then
call bob_set_error(BOB_ERROR_INVALID_ARGUMENT, &
"NTT size must be power of 2", "goldilocks_ntt")
status = BOB_ERROR_INVALID_ARGUMENT; return
end if
! Bit-reverse permutation
call bit_reverse_permute(a, n)
! Butterfly layers
len = 2
do while (len <= n)
half = len / 2
! Root of unity: g^((p-1)/len) mod p
root_pow = (GOLDILOCKS_P - 1_i8) / int(len, i8)
omega = goldilocks_from_canonical(GOLDILOCKS_G)
omega = omega%pow(root_pow)
call goldilocks_fft_layer(a, n, half, omega)
len = len * 2
end do
end subroutine goldilocks_ntt
!> In-place inverse NTT
subroutine goldilocks_intt(a, n, status)
type(goldilocks_t), intent(inout) :: a(n)
integer(i4), intent(in) :: n
integer(c_int32_t), intent(out) :: status
type(goldilocks_t) :: omega, n_inv
integer(i8) :: root_pow
integer(i4) :: len, half, i
status = BOB_SUCCESS
if (n <= 1) return
if (iand(n, n-1) /= 0) then
status = BOB_ERROR_INVALID_ARGUMENT; return
end if
call bit_reverse_permute(a, n)
len = 2
do while (len <= n)
half = len / 2
root_pow = (GOLDILOCKS_P - 1_i8) / int(len, i8)
! Use inverse root: g^(p-1 - (p-1)/len)
omega = goldilocks_from_canonical(GOLDILOCKS_G)
omega = omega%pow(GOLDILOCKS_P - 1_i8 - root_pow)
call goldilocks_fft_layer(a, n, half, omega)
len = len * 2
end do
! Divide by n
n_inv = goldilocks_new(int(n, i8))
n_inv = n_inv%inv()
do i = 1, n
a(i) = a(i)%mul(n_inv)
end do
end subroutine goldilocks_intt
!> Bit-reverse permutation
pure subroutine bit_reverse_permute(a, n)
type(goldilocks_t), intent(inout) :: a(n)
integer(i4), intent(in) :: n
type(goldilocks_t) :: tmp
integer(i4) :: i, j, k, bits
bits = 0; k = n
do while (k > 1); bits = bits + 1; k = k / 2; end do
j = 0
do i = 1, n - 1
k = n / 2
do while (iand(j, k) /= 0); j = ieor(j, k); k = k / 2; end do
j = ieor(j, k)
if (i < j + 1) then
tmp = a(i + 1); a(i + 1) = a(j + 1); a(j + 1) = tmp
end if
end do
end subroutine bit_reverse_permute
!══════════════════════════════════════════════════════════════════
! C ABI
!══════════════════════════════════════════════════════════════════
function bob_gf_new(val) result(out) bind(C, name="bob_gf_new")
integer(c_int64_t), value :: val
integer(c_int64_t) :: out
out = goldilocks_reduce(val)
end function bob_gf_new
function bob_gf_add(a, b) result(out) bind(C, name="bob_gf_add")
integer(c_int64_t), value :: a, b
integer(c_int64_t) :: out
type(goldilocks_t) :: fa, fb, tmp_gf
fa = goldilocks_from_canonical(a)
fb = goldilocks_from_canonical(b)
tmp_gf = fa%add(fb); out = tmp_gf%val
end function bob_gf_add
function bob_gf_mul(a, b) result(out) bind(C, name="bob_gf_mul")
integer(c_int64_t), value :: a, b
integer(c_int64_t) :: out
type(goldilocks_t) :: fa, fb, tmp_gf
fa = goldilocks_from_canonical(a)
fb = goldilocks_from_canonical(b)
tmp_gf = fa%mul(fb); out = tmp_gf%val
end function bob_gf_mul
function bob_gf_inv(a) result(out) bind(C, name="bob_gf_inv")
integer(c_int64_t), value :: a
integer(c_int64_t) :: out
type(goldilocks_t) :: fa, tmp_gf
fa = goldilocks_from_canonical(a)
tmp_gf = fa%inv(); out = tmp_gf%val
end function bob_gf_inv
function bob_gf_pow(a, exp) result(out) bind(C, name="bob_gf_pow")
integer(c_int64_t), value :: a, exp
integer(c_int64_t) :: out
type(goldilocks_t) :: fa, tmp_gf
fa = goldilocks_from_canonical(a)
tmp_gf = fa%pow(exp); out = tmp_gf%val
end function bob_gf_pow
function bob_gf_ntt(arr_ptr, n) result(status) bind(C, name="bob_gf_ntt")
type(c_ptr), value :: arr_ptr
integer(c_int32_t), value :: n
integer(c_int32_t) :: status
integer(i8), pointer :: arr(:)
type(goldilocks_t), allocatable :: gf(:)
integer(i4) :: i
call c_f_pointer(arr_ptr, arr, [n])
allocate(gf(n))
do i = 1, n; gf(i) = goldilocks_from_canonical(arr(i)); end do
call goldilocks_ntt(gf, n, status)
do i = 1, n; arr(i) = gf(i)%val; end do
deallocate(gf)
end function bob_gf_ntt
function bob_gf_intt(arr_ptr, n) result(status) bind(C, name="bob_gf_intt")
type(c_ptr), value :: arr_ptr
integer(c_int32_t), value :: n
integer(c_int32_t) :: status
integer(i8), pointer :: arr(:)
type(goldilocks_t), allocatable :: gf(:)
integer(i4) :: i
call c_f_pointer(arr_ptr, arr, [n])
allocate(gf(n))
do i = 1, n; gf(i) = goldilocks_from_canonical(arr(i)); end do
call goldilocks_intt(gf, n, status)
do i = 1, n; arr(i) = gf(i)%val; end do
deallocate(gf)
end function bob_gf_intt
end module bob_goldilocks
! Made with Bob