|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
|
|
|
|
| integer(i8), parameter, public :: GOLDILOCKS_P = int(Z'FFFFFFFF00000001', i8)
|
|
|
| integer(i8), parameter, public :: GOLDILOCKS_G = 7_i8
|
|
|
| integer(i4), parameter, public :: GOLDILOCKS_TWO_ADICITY = 32
|
|
|
|
|
| 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
|
| public :: goldilocks_intt
|
| public :: goldilocks_fft_layer
|
| public :: gf_add, gf_sub, gf_mul, gf_neg, gf_inv, gf_pow
|
|
|
|
|
| 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
|
|
|
|
|
|
|
|
|
| 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
|
|
|
|
|
|
|
|
|
| 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
|
|
|
|
|
|
|
|
|
|
|
|
|
| pure function goldilocks_reduce(val) result(r)
|
| integer(i8), intent(in) :: val
|
| integer(i8) :: r
|
| r = val
|
|
|
| if (r >= GOLDILOCKS_P) then
|
| r = r - GOLDILOCKS_P
|
| end if
|
|
|
| if (r >= GOLDILOCKS_P) then
|
| r = r - GOLDILOCKS_P
|
| end if
|
| end function goldilocks_reduce
|
|
|
|
|
|
|
|
|
|
|
| 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
|
|
|
| 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)
|
|
|
| cross1 = a_lo * b_hi
|
| cross2 = a_hi * b_lo
|
| cross = cross1 + cross2
|
| hi = a_hi * b_hi + ishft(cross, -32)
|
|
|
| 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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| pure function gf_mul(this, other) result(r)
|
| class(goldilocks_t), intent(in) :: this, other
|
| type(goldilocks_t) :: r
|
| integer(i8) :: lo, hi, adj
|
|
|
| lo = this%val * other%val
|
| hi = goldilocks_mul_hi(this%val, other%val)
|
|
|
|
|
| adj = ishft(hi, 32) - hi
|
|
|
| r%val = goldilocks_reduce(lo + adj)
|
| end function gf_mul
|
|
|
|
|
|
|
|
|
| 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
|
|
|
|
|
|
|
|
|
| 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
|
|
|
|
|
|
|
|
|
| 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
|
|
|
|
|
|
|
|
|
|
|
| pure function gf_inv(this) result(r)
|
| class(goldilocks_t), intent(in) :: this
|
| type(goldilocks_t) :: r
|
|
|
|
|
|
|
| type(goldilocks_t) :: x, t
|
| integer(i4) :: i
|
| x = this
|
| t = goldilocks_from_canonical(1_i8)
|
|
|
|
|
|
|
|
|
|
|
|
|
| x = this
|
| do i = 1, 31
|
| x = x%mul(x)
|
| end do
|
| t = x%mul(this)
|
|
|
|
|
| x = this
|
| t = goldilocks_from_canonical(1_i8)
|
| call gf_pow_impl(x, GOLDILOCKS_P - 2_i8, t)
|
| r = t
|
| end function gf_inv
|
|
|
|
|
|
|
|
|
| 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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 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
|
|
|
|
|
| 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
|
|
|
| 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
|
|
|
| call bit_reverse_permute(a, n)
|
|
|
| len = 2
|
| do while (len <= n)
|
| half = len / 2
|
|
|
| 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
|
|
|
|
|
| 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)
|
|
|
| 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
|
|
|
| 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
|
|
|
|
|
| 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
|
|
|
|
|
|
|
|
|
|
|
| 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
|
|
|
|
|
|
|