123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790(*****************************************************************************)(* *)(* MIT License *)(* Copyright (c) 2022 Nomadic Labs <contact@nomadic-labs.com> *)(* *)(* Permission is hereby granted, free of charge, to any person obtaining a *)(* copy of this software and associated documentation files (the "Software"),*)(* to deal in the Software without restriction, including without limitation *)(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *)(* and/or sell copies of the Software, and to permit persons to whom the *)(* Software is furnished to do so, subject to the following conditions: *)(* *)(* The above copyright notice and this permission notice shall be included *)(* in all copies or substantial portions of the Software. *)(* *)(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *)(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *)(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *)(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *)(* DEALINGS IN THE SOFTWARE. *)(* *)(*****************************************************************************)moduleFr=Bls12_381.FrmoduleStubs=structtypefr=Fr.ttypefr_array=Fr_carray.t(** [add res a b size_a size_b] writes the result of polynomial addition
of [a] and [b] using the evaluation representation in [res], where
- [a] is evaluated on [domain_a] of size [size_a]
- [b] is evaluated on [domain_b] of size [size_b]
requires:
- [size a = size_a]
- [size b = size_b]
- [size res = min (size_a, size_b)]
- [res], [a] and [b] are either pairwise disjoint or equal
- [size_b mod size_a = 0] *)externaladd:fr_array->fr_array->fr_array->int->int->unit="caml_bls12_381_polynomial_polynomial_evaluations_add_stubs"[@@noalloc](** [mul_arrays res eval_evallen_comp_power_powlen size_res nb_evals] writes
the result of computing [p₁(gᶜ₁·x)ᵐ₁·p₂(gᶜ₂·x)ᵐ₂·…·pₖ(gᶜₖ·x)ᵐₖ] using
the evaluation representation in [res], where
- [eval_evallen_comp_power_powlen.[i] = (pᵢ, size_p_i, cᵢ, mᵢ, size_bits_m_i)]
- a polynomial [pᵢ] is evaluated on [domain_p_i] of size [size_p_i]
- [cᵢ] is a composition_gx; it computes [pᵢ(gᶜᵢ·x)] instead of [pᵢ(x)],
where [g] is a primitive [size_p_i]-th root of unity
- [mᵢ] is a power in [pᵢ(x)ᵐᵢ]
- [size_bits_m_i] is the *exact* number of bits in [mᵢ]
- [nb_evals] is a number of evaluations, i.e., [i = 1..nb_evals]
requires:
- [size res = size_res]
- [size eval_evallen_comp_power_powlen = nb_evals]
- [size p_i = size_p_i]
- [size_bits m_i = size_bits_m_i]
- [size_p_i mod size_res = 0]
- [res] and [p_i] are disjoint *)externalmul_arrays:fr_array->(fr_array*int*int*Bytes.t*int)array->int->int->unit="caml_bls12_381_polynomial_polynomial_evaluations_mul_arrays_stubs"[@@noalloc](** [linear_arrays res eval_evallen_coeff_comp add_constant size_res nb_evals]
writes the result of computing
[λ₁·p₁(gᶜ₁·x) + λ₂·p₂(gᶜ₂·x) + … + λₖ·pₖ(gᶜₖ·x) + add_constant] using
the evaluation representation in [res], where
- [eval_evallen_coeff_comp.[i] = (pᵢ, size_p_i, λᵢ, cᵢ)]
- a polynomial [pᵢ] is evaluated on [domain_p_i] of size [size_p_i]
- [cᵢ] is a composition_gx; it computes [pᵢ(gᶜᵢ·x)] instead of [pᵢ(x)],
where [g] is a primitive [size_p_i]-th root of unity
- [λᵢ] is a coefficient in [λᵢ·pᵢ(x)]
- [nb_evals] is a number of evaluations, i.e., [i = 1..nb_evals]
requires:
- [size res = size_res]
- [size eval_evallen_coeff_comp = nb_evals]
- [size p_i = size_p_i]
- [size_p_i mod size_res = 0]
- [res] and [p_i] are disjoint *)externallinear_arrays:fr_array->(fr_array*int*fr*int)array->fr->int->int->unit="caml_bls12_381_polynomial_polynomial_evaluations_linear_arrays_stubs"[@@noalloc](** [fft_inplace p domain log log_degree] computes Fast Fourier Transform.
It converts the coefficient representation of a polynomial [p] to
the evaluation representation
requires:
- [size p = size domain]
- [size domain = 2^log]
- [domain = [one; g; ..; g^{n-1}]] where [g] is a primitive
[n]-th root of unity and [n = 2^log] (as done by {!Domain.Stubs.compute_domain}) *)externalfft_inplace:fr_array->domain:fr_array->log:int->log_degree:int->unit="caml_bls12_381_polynomial_fft_inplace_on_stubs"(** [ifft_inplace p domain log] computes Inverse Fast Fourier Transform.
It converts the evaluation representation of a polynomial [p] to
the coefficient representation
requires:
- [size p = size domain]
- [size domain = 2^log]
- [domain = [one; g; ..; g^{n-1}]] where [g] is a primitive
[n]-th root of unity and [n = 2^log] (as done by {!Domain.Stubs.compute_domain}) *)externalifft_inplace:fr_array->domain:fr_array->log:int->unit="caml_bls12_381_polynomial_ifft_inplace_on_stubs"[@@noalloc](** [dft_inplace coefficients domain inverse length] computes the Fourier Transform.
requires:
- [size domain = size coefficients = length]
- [length <= 2^10]
- [length != 2^k]
- if [inverse = true] then the inverse dft is performed
- [domain = [one; g; ..; g^{n-1}]] where [g] is a primitive
[n]-th root of unity (as done by {!Domain.Stubs.compute_domain}) *)externaldft_inplace:fr_array->fr_array->bool->int->unit="caml_bls12_381_polynomial_dft_stubs"[@@noalloc](** [fft_prime_factor_algorithm_inplace coefficient (domain1, domain1_length) (domain2, domain2_length) inverse]
computes the Fast Fourier Transform following
{{: https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm }the Prime-factor FFT algorithm}.
requires:
- [size domain1 = domain1_length]
- [size domain2 = domain2_length]
- [size domain1] and [size domain2] to be coprime
- if for some k [size domain1 != 2^k] then [size domain1 <= 2^10]
- if for some k [size domain2 != 2^k] then [size domain2 <= 2^10]
- [size coefficients = domain1_length * domain2_length]
- if [inverse = true] then the inverse fft is performed
- [domain = [one; g; ..; g^{n-1}]] where [g] is a primitive
[n]-th root of unity (as done by {!Domain.Stubs.compute_domain}) *)externalfft_prime_factor_algorithm_inplace:fr_array->fr_array*int->fr_array*int->bool->unit="caml_bls12_381_polynomial_prime_factor_algorithm_fft_stubs"[@@noalloc]endmoduletypeEvaluations_sig=sigtypescalartypepolynomialtypet[@@derivingrepr](** [init size f degree] initializes an evaluation of a polynomial of the given
[degree]. *)valinit:int->(int->scalar)->degree:int->t(** [of_array (d, e)] creates a value of type [t] from the evaluation
representation of a polynomial [e] of degree [d], i.e, it converts an OCaml
array to a C array *)valof_array:int*scalararray->t(** [to_array] converts a C array to an OCaml array *)valto_array:t->scalararray(** [string_of_eval e] returns the string representation of evaluation [e] *)valstring_of_eval:t->stringtypedomain(** [of_domain d] converts [d] from type {!type:domain} to type {!type:t}.
Note: [of_domain d] doesn't create a copy of [d] *)valof_domain:domain->t(** [to_domain d] converts [d] from type {!type:t} to type {!type:domain}.
Note: [to_domain d] doesn't create a copy of [d] *)valto_domain:t->domain(** [zero] returns the evaluation representation of the zero polynomial *)valzero:t(** [is_zero p] checks whether a polynomial [p] is the zero polynomial *)valis_zero:t->bool(** [degree] returns the degree of a polynomial. Returns [-1] for the zero polynomial *)valdegree:t->int(** [length e] returns the size of domain where a polynomial is evaluated, or equally,
the size of a C array where evaluation [e] is stored *)vallength:t->int(** [create len] returns the evaluation representation of a zero polynomial of size [len] *)valcreate:int->t(** [copy ?res a] returns a copy of evaluation [a]. The function writes the result in [res]
if [res] has the correct size and allocates a new array for the result otherwise *)valcopy:?res:t->t->t(** [get p i] returns the [i]-th element of a given array [p] *)valget:t->int->scalar(** [get_inplace p i res] copies the [i]-th element of a given array [p] in res *)valget_inplace:t->int->scalar->unit(** [mul_by_scalar] computes muliplication of a polynomial by a blst_fr element *)valmul_by_scalar:scalar->t->t(** [mul_c] computes [p₁(gᶜ₁·x)ᵐ₁·p₂(gᶜ₂·x)ᵐ₂·…·pₖ(gᶜₖ·x)ᵐₖ], where
- [pᵢ = List.nth evaluations i]
- [mᵢ = List.nth powers i]
- [cᵢ = List.nth (fst composition_gx) i]
- [n = snd composition_gx] is the order of generator, i.e., [gⁿ = 1]
The function writes the result in [res] if [res] has the correct size (= min (size pᵢ))
and allocates a new array for the result otherwise
Note: [res] and [pᵢ] are disjoint *)valmul_c:?res:t->evaluations:tlist->?composition_gx:intlist*int->?powers:intlist->unit->t(** [linear_c] computes [λ₁·p₁(gᶜ₁·x) + λ₂·p₂(gᶜ₂·x) + … + λₖ·pₖ(gᶜₖ·x) + add_constant], where
- [pᵢ = List.nth evaluations i]
- [λᵢ = List.nth linear_coeffs i]
- [cᵢ = List.nth (fst composition_gx) i]
- [n = snd composition_gx] is the order of generator, i.e., [gⁿ = 1]
The function writes the result in [res] if [res] has the correct size (= min (size pᵢ))
and allocates a new array for the result otherwise
Note: [res] and [pᵢ] are disjoint *)vallinear_c:?res:t->evaluations:tlist->?linear_coeffs:scalarlist->?composition_gx:intlist*int->?add_constant:scalar->unit->t(** [linear_with_powers p s] computes [∑ᵢ sⁱ·p.(i)]. This function is more efficient
than [linear] + [powers] for evaluations of the same size *)vallinear_with_powers:tlist->scalar->t(** [add ?res a b] computes polynomial addition of [a] and [b]. The function writes
the result in [res] if [res] has the correct size (= min (size (a, b))) and allocates
a new array for the result otherwise
Note: [res] can be equal to either [a] or [b] *)valadd:?res:t->t->t->t(** [equal a b] checks whether a polynomial [a] is equal to a polynomial [b]
Note: [equal] is defined as restrictive equality, i.e., the same polynomial
evaluated on different domains are said to be different *)valequal:t->t->bool(** [evaluation_fft domain p] converts the coefficient representation of
a polynomial [p] to the evaluation representation. [domain] can be obtained
using {!Domain.build}
Note:
- size of domain must be a power of two
- degree of polynomial must be strictly less than the size of domain *)valevaluation_fft:domain->polynomial->t(** [interpolation_fft domain p] converts the evaluation representation of
a polynomial [p] to the coefficient representation. [domain] can be obtained
using {!Domain.build}
Note:
- size of domain must be a power of two
- size of a polynomial must be equal to size of domain *)valinterpolation_fft:domain->t->polynomial(* TODO DELETE *)valinterpolation_fft2:domain->scalararray->polynomial(** [dft domain polynomial] converts the coefficient representation of
a polynomial [p] to the evaluation representation. [domain] can be obtained
using {!Domain.build}.
Computes the discrete Fourier transform in time O(n^2)
where [n = size domain].
requires:
- [size domain] to divide Bls12_381.Fr.order - 1
- [size domain != 2^k]
- [degree polynomial < size domain] *)valdft:domain->polynomial->t(** [idft domain t] converts the evaluation representation of
a polynomial [p] to the coefficient representation. [domain] can be obtained
using {!Domain.build}.
Computes the inverse discrete Fourier transform in time O(n^2)
where [n = size domain].
requires:
- [size domain] to divide Bls12_381.Fr.order - 1
- [size domain != 2^k]
- [size domain = size t] *)validft:domain->t->polynomial(** [evaluation_fft_prime_factor_algorithm domain1 domain2 p] converts the
coefficient representation of a polynomial [p] to the evaluation representation.
[domain] can be obtained using {!Domain.build}.
See {{: https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm }the Prime-factor FFT algorithm}.
requires:
- [size domain1 * size domain2] to divide Bls12_381.Fr.order - 1
- [size domain1] and [size domain2] to be coprime
- if for some k [size domain1 != 2^k] then [size domain1 <= 2^10]
- if for some k [size domain2 != 2^k] then [size domain2 <= 2^10]
- [degree polynomial < size domain1 * size domain2] *)valevaluation_fft_prime_factor_algorithm:domain1:domain->domain2:domain->polynomial->t(** [interpolation_fft_prime_factor_algorithm domain1 domain2 t] converts the
evaluation representation of a polynomial [p] to the coefficient representation.
[domain] can be obtained using {!Domain.build}.
See {{: https://en.wikipedia.org/wiki/Prime-factor_FFT_algorithm }the Prime-factor FFT algorithm}.
requires:
- [size domain1 * size domain2] to divide Bls12_381.Fr.order - 1
- [size domain1] and [size domain2] to be coprime
- if for some k [size domain1 != 2^k] then [size domain1 <= 2^10]
- if for some k [size domain2 != 2^k] then [size domain2 <= 2^10]
- [size t = size domain1 * size domain2] *)valinterpolation_fft_prime_factor_algorithm:domain1:domain->domain2:domain->t->polynomialendmoduleEvaluations_impl=structmoduleDomain_c=Domain.StubsmoduleDomain=Domain.Domain_unsafemodulePolynomial_c=Polynomial.Stubs(* TODO remove *)modulePolynomial=Polynomial.Polynomial_unsafetypescalar=Fr.ttypepolynomial=Polynomial.t(* degree & evaluations *)typet=int*Fr_carray.t[@@derivingrepr]typedomain=Domain.tletinitsizef~degree=(degree,Fr_carray.initsizef)letof_array(d,p)=ifd<-1thenraise@@Invalid_argument"make_evaluation: degree must be >= -1";ifArray.lengthp<=dthenraise@@Invalid_argument"make_evaluation: array must be longer than degree";letres=Fr_carray.of_arraypin(d,res)letto_array(_d,e)=Fr_carray.to_arrayeletto_carray(_,e)=eletstring_of_eval(d,e)=Printf.sprintf"%d : [%s]"dPolynomial.(to_string(of_carraye))letof_domaindomain=letd=Domain.to_carraydomainin(1,d)letallocate=Fr_carray.allocateletto_domain(_,eval)=Domain.of_carrayevalletzero=(-1,allocate1)letdegree(d,_)=dletlength(_,e)=Fr_carray.lengtheletcreaten=(-1,allocaten)letis_zero(d,_e)=(* if a degree is not included in the definition of evaluations,
use Fr_carray.Stubs.is_zero e l *)d=-1letallocate_for_resreslength_result=matchreswith|Some(_,res)whenFr_carray.lengthres=length_result->res|_->allocatelength_resultletcopy?res(d,e)=letlen=Fr_carray.lengtheinletres=allocate_for_resresleninFr_carray.blite~src_off:0res~dst_off:0~len;(d,res)letget(_,eval)i=Fr_carray.getevaliletget_inplace(_,eval)ires=Fr_carray.get_inplaceevaliresletmul_by_scalarlambda(d,e)=letlen=Fr_carray.lengtheinletres=allocateleninPolynomial_c.mul_by_scalarreslambdaelen;(d,res)(* multiplies evaluations of all polynomials with name in poly_names,
the resulting eval has the size of the smallest evaluation *)letmul_c?res~evaluations?composition_gx?powers()=letlen_evaluations=List.lengthevaluationsinletcomposition_gx=matchcomposition_gxwith|Somecomposition_gx->composition_gx|None->(List.initlen_evaluations(fun_->0),1)inletpowers=matchpowerswith|Somepowers->powers|None->List.initlen_evaluations(fun_->1)inletdomain_len=sndcomposition_gxinassert(domain_len>0);assert(len_evaluations>0);assert(List.compare_length_with(fstcomposition_gx)len_evaluations=0);assert(List.compare_length_withpowerslen_evaluations=0);assert(List.for_all(funpower->power>0)powers);letlength_result=List.fold_leftminInt.max_int@@List.maplengthevaluationsinletres=allocate_for_resreslength_resultinifList.existsis_zeroevaluationsthen(Fr_carray.erasereslength_result;(-1,res))elseletdegree_result=List.fold_left2(funaccdpow->acc+(d*pow))0(List.mapdegreeevaluations)powersinifdegree_result>=length_resultthenraise(Invalid_argument(Printf.sprintf"mul_evaluations : evaluation is too short (length=%d) for \
expected result size %d"length_result(degree_result+1)))elseletlist_array=List.map2(fun(evaluation,pow)composition->letpow=Z.of_intpowinletpow_bytes=Z.to_bitspow|>Bytes.unsafe_of_stringinletpow_len=Z.numbitspowinletl=lengthevaluationinletrescale_composition=composition*l/domain_lenin(sndevaluation,l,rescale_composition,pow_bytes,pow_len))(List.combineevaluationspowers)(fstcomposition_gx)inletnb_evals=List.lengthevaluationsinStubs.mul_arraysres(Array.of_listlist_array)length_resultnb_evals;(degree_result,res)letconstantpc=fori=0toFr_carray.lengthp-1doFr_carray.setpcidone(* Adds evaluation of a1 × p1 + a2 × p2 in evaluations
/!\ the degree may not be always accurate,
the resulting degree may not be the max of the 2 polynomials degrees *)letlinear_c?res~evaluations?linear_coeffs?composition_gx?(add_constant=Fr.zero)()=letlen_evaluations=List.lengthevaluationsinletlinear_coeffs=matchlinear_coeffswith|Somelinear_coeffs->linear_coeffs|None->List.initlen_evaluations(fun_->Fr.(copyone))inletcomposition_gx=matchcomposition_gxwith|Somecomposition_gx->composition_gx|None->(List.initlen_evaluations(fun_->0),1)inletdomain_len=sndcomposition_gxinassert(domain_len>0);assert(len_evaluations>0);assert(List.compare_length_withlinear_coeffslen_evaluations=0);assert(List.compare_length_with(fstcomposition_gx)len_evaluations=0);letlist_eval_coeff_composition=List.map2(fun(eval,coeff)composition->letrescale_composition=composition*lengtheval/domain_lenin(eval,coeff,rescale_composition))(List.combineevaluationslinear_coeffs)(fstcomposition_gx)|>List.filter(fun(eval,_,_)->not(is_zeroeval))inmatchlist_eval_coeff_compositionwith|[]->letlength_result=List.fold_leftminInt.max_int@@List.maplengthevaluationsinletres=allocate_for_resreslength_resultinconstantresadd_constant;letdegree_result=ifFr.is_zeroadd_constantthen-1else0in(degree_result,res)|_::_->letlength_result=List.fold_leftminInt.max_int@@List.map(fun(eval,_,_)->lengtheval)list_eval_coeff_compositioninletdegree_result=List.fold_leftmax0@@List.map(fun(eval,_,_)->degreeeval)list_eval_coeff_compositionin(* TODO: check relation between length_result and degree_result? *)letnb_evals=List.lengthlist_eval_coeff_compositioninletarray_eval_coeff_composition=List.map(fun(eval,linear_coeff,composition)->(sndeval,lengtheval,linear_coeff,composition))list_eval_coeff_composition|>Array.of_listinletres=allocate_for_resreslength_resultinStubs.linear_arraysresarray_eval_coeff_compositionadd_constantlength_resultnb_evals;(degree_result,res)(* Adds 2 evaluations *)letadd?rese1e2=letd1=fste1inletd2=fste2inletl1=lengthe1inletl2=lengthe2inifd1=-1thenletres=allocate_for_resresl2incopy~res:(d2,res)e2elseifd2=-1thenletres=allocate_for_resresl1incopy~res:(d1,res)e1elseletdeg_result=maxd1d2inletlength_result=minl1l2inletres=allocate_for_resreslength_resultinStubs.addres(snde1)(snde2)l1l2;(deg_result,res)letlinear_with_powersevalscoeff=letnb_evals=List.lengthevalsinassert(nb_evals>0);leteval_lenghts=List.maplengthevalsinleteval0_length=List.hdeval_lenghtsinletis_equal_size=List.for_all(Int.equaleval0_length)eval_lenghtsinifis_equal_sizethen(letlength_result=eval0_lengthinletdegree_result=List.fold_leftmax(-1)@@List.mapdegreeevalsinifdegree_result=-1thencreatelength_resultelseletres=allocatelength_resultinletevals=List.map(fun(_,e)->(e,Fr_carray.lengthe))evals|>Array.of_listinPolynomial_c.linear_with_powersrescoeffevalsnb_evals;(degree_result,res))elseletcoeffs=Fr_carray.powersnb_evalscoeff|>Array.to_listinlinear_c~evaluations:evals~linear_coeffs:coeffs()(*restrictive equality, the same polynomial evaluated
on different domains are said to be different*)letequal(deg1,eval1)(deg2,eval2)=ifdeg1<>deg2||Fr_carray.(lengtheval1<>lengtheval2)thenfalseelsePolynomial.(equal(of_carrayeval1)(of_carrayeval2))letevaluation_fft_internal:Domain.t->polynomial->Fr_carray.t=fundomainpoly->letdegree=Polynomial.degreepolyinletlog_degree=Z.log2up(Z.of_int(degree+1))inletdomain=Domain.to_carraydomaininletn_domain=Fr_carray.lengthdomaininletpoly=Polynomial.to_carraypolyinletlog=Z.(log2up@@of_intn_domain)inifnot(Helpers.is_power_of_twon_domain)thenraise@@Invalid_argument"Size of domain should be a power of 2.";ifnot(degree<n_domain)thenraise@@Invalid_argument"Degree of poly should be strictly less than domain size.";letres=allocaten_domaininFr_carray.blitpoly~src_off:0res~dst_off:0~len:(degree+1);Stubs.fft_inplaceres~domain~log~log_degree;resletevaluation_fft:domain->polynomial->t=fundomainpoly->letd=Polynomial.degreepolyinletdomain_length=Domain.lengthdomaininifd=-1then(d,allocatedomain_length)elseletres=evaluation_fft_internaldomainpolyin(d,res)letinterpolation_fft_internal:Domain.t->Fr_carray.t->polynomial=fundomaincoefficients->letdomain=Domain.to_carraydomaininletn_domain=Fr_carray.lengthdomaininletlog=Z.(log2up@@of_intn_domain)inifnot(Helpers.is_power_of_twon_domain)thenraise@@Invalid_argument"Size of domain should be a power of 2.";letn_coefficients=Fr_carray.lengthcoefficientsinifnot(n_coefficients=n_domain)thenraise@@Invalid_argument"Size of coefficients should be same as domain.";Stubs.ifft_inplacecoefficients~domain~log;Polynomial.of_carraycoefficientsletinterpolation_fft:domain->t->polynomial=fundomain(d,evaluation)->ifd=-1thenPolynomial.zeroelseletlength_res=Domain.lengthdomaininletrescaled_eval=allocatelength_resinDomain_c.rescalerescaled_evalevaluationlength_res(Fr_carray.lengthevaluation);interpolation_fft_internaldomainrescaled_evalletinterpolation_fft2:Domain.t->scalararray->polynomial=fundomaincoefficients->interpolation_fft_internaldomain(Fr_carray.of_arraycoefficients)letdftdomainpolynomial=letlength=Domain.lengthdomaininiflength>1lsl10thenraise@@Invalid_argument"Domain size must be <= 2^10.";ifHelpers.is_power_of_twolengththenraise@@Invalid_argument"Domain size must not be a power of two";letd=Polynomial.degreepolynomialinletpolynomial=Polynomial.to_carraypolynomialinifnot(d<length)thenraise@@Invalid_argument"Degree of poly should be strictly less than domain size.";letevaluations=allocatelengthinFr_carray.blitpolynomial~src_off:0evaluations~dst_off:0~len:(d+1);Stubs.dft_inplaceevaluations(Domain.to_carraydomain)falselength;(d,evaluations)letidftdomain(_,evaluations)=letlength=Domain.lengthdomaininiflength>1lsl10thenraise@@Invalid_argument"Domain size must be <= 2^10.";ifHelpers.is_power_of_twolengththenraise@@Invalid_argument"Domain size must not be a power of two";ifnot(length=Fr_carray.lengthevaluations)thenraise@@Invalid_argument"Size of coefficients should be same as domain.";letcoefficients=Fr_carray.copyevaluationsinStubs.dft_inplacecoefficients(Domain.to_carraydomain)truelength;Polynomial.of_carraycoefficientsletevaluation_fft_prime_factor_algorithm~domain1~domain2polynomial=letdomain1_length=Domain.lengthdomain1inletdomain2_length=Domain.lengthdomain2inif(not(Helpers.is_power_of_twodomain1_length))&&domain1_length>1lsl10thenraise@@Invalid_argument"Domain of non power of 2 length must be <= 2^10.";if(not(Helpers.is_power_of_twodomain2_length))&&domain2_length>1lsl10thenraise@@Invalid_argument"Domain of non power of 2 length must be <= 2^10.";ifnotZ.(gcd(of_intdomain1_length)(of_intdomain2_length)=one)thenraise@@Invalid_argument"Size of domains must be coprime.";letn_domain=domain1_length*domain2_lengthinletd=Polynomial.degreepolynomialinletcoefficients=Polynomial.to_carraypolynomialinifnot(d<n_domain)thenraise@@Invalid_argument"Degree of poly should be strictly less than domain size.";letres=allocaten_domaininifd=-1then(d,res)elseletdomain1=Domain.to_carraydomain1inletdomain2=Domain.to_carraydomain2inFr_carray.blitcoefficients~src_off:0res~dst_off:0~len:(d+1);Stubs.fft_prime_factor_algorithm_inplaceres(domain1,domain1_length)(domain2,domain2_length)false;(d,res)letinterpolation_fft_prime_factor_algorithm~domain1~domain2(d,evaluations)=letdomain1_length=Domain.lengthdomain1inletdomain2_length=Domain.lengthdomain2inif(not(Helpers.is_power_of_twodomain1_length))&&domain1_length>1lsl10thenraise@@Invalid_argument"Domain of non power of 2 length must be <= 2^10.";if(not(Helpers.is_power_of_twodomain2_length))&&domain2_length>1lsl10thenraise@@Invalid_argument"Domain of non power of 2 length must be <= 2^10.";ifnotZ.(gcd(of_intdomain1_length)(of_intdomain2_length)=one)thenraise@@Invalid_argument"Size of domains must be coprime.";letn_domain=domain1_length*domain2_lengthinletn_evaluations=Fr_carray.lengthevaluationsinifnot(n_evaluations=n_domain)thenraise@@Invalid_argument"Size of coefficients should be same as domain.";ifd=-1thenPolynomial.zeroelseletdomain1=Domain.to_carraydomain1inletdomain2=Domain.to_carraydomain2inletcoefficients=Fr_carray.copyevaluationsinStubs.fft_prime_factor_algorithm_inplacecoefficients(domain1,domain1_length)(domain2,domain2_length)true;Polynomial.of_carraycoefficientsendmoduletypeEvaluations_unsafe_sig=sigincludeEvaluations_sig(** [to_carray t] converts [t] from type {!type:t} to type {!type:Fr_carray.t}
Note: [to_carray t] doesn't create a copy of [t] *)valto_carray:t->Fr_carray.tendmoduleEvaluations_unsafe:Evaluations_unsafe_sigwithtypescalar=Bls12_381.Fr.tandtypedomain=Domain.tandtypepolynomial=Polynomial.t=Evaluations_implinclude(Evaluations_unsafe:Evaluations_sigwithtypet=Evaluations_unsafe.tandtypescalar=Evaluations_unsafe.scalarandtypedomain=Evaluations_unsafe.domainandtypepolynomial=Evaluations_unsafe.polynomial)