123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640(*****************************************************************************)(* *)(* 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(** [of_sparse res p n] converts the sparse representation of a polynomial [p] to
the dense representation, from an OCaml array [p] of size [n] to a C array [res] of
size [degree p + 1]
requires:
- degree of each coeff [d_i >= 0] and [d_i] are unique
- the result must be initialized with zero (as done by {!Fr_carray.allocate})
- [size res = degree p + 1]
- [size p = n] *)externalof_sparse:fr_array->(fr*int)array->int->unit="caml_bls12_381_polynomial_polynomial_of_sparse_stubs"[@@noalloc](** [add res a b size_a size_b] writes the result of polynomial addition of [a] and [b]
in [res]
requires:
- [size a = size_a]
- [size b = size_b]
- [size res = max (size_a, size_b)]
- [res], [a] and [b] are either pairwise disjoint or equal *)externaladd:fr_array->fr_array->fr_array->int->int->unit="caml_bls12_381_polynomial_polynomial_add_stubs"[@@noalloc](** [sub res a b size_a size_b] writes the result of polynomial subtraction of [b] from [a]
in [res]
requires:
- [size a = size_a]
- [size b = size_b]
- [size res = max (size_a, size_b)]
- [res], [a] and [b] are either pairwise disjoint or equal *)externalsub:fr_array->fr_array->fr_array->int->int->unit="caml_bls12_381_polynomial_polynomial_sub_stubs"[@@noalloc](** [mul res a b size_a size_b] writes the result of polynomial multiplication of [a] by [b]
in [res]
requires:
- the result must be initialized with zero (as done by {!Fr_carray.allocate})
- [size a = size_a]
- [size b = size_b]
- [size res = size_a + size_b - 1] *)externalmul:fr_array->fr_array->fr_array->int->int->unit="caml_bls12_381_polynomial_polynomial_mul_stubs"[@@noalloc](** [mul_by_scalar res b a size_a] writes the result of multiplying a polynomial [a]
by a blst_fr element [b] in [res]
requires:
- [size a = size_a]
- [size res = size_a]
- [res] and [a] either disjoint or equal *)externalmul_by_scalar:fr_array->fr->fr_array->int->unit="caml_bls12_381_polynomial_polynomial_mul_by_scalar_stubs"[@@noalloc](** [linear res poly_polylen_coeff nb_polys] writes the result of
computing [λ₁·p₁(x) + λ₂·p₂(x) + … + λₖ·pₖ(x)] in [res], where
- [poly_polylen_coeff.[i] = (pᵢ, size_p_i, λᵢ)]
- [nb_polys] is a number of polynomials, i.e., [i = 1..nb_polys]
requires:
- the result must be initialized with zero (as done by {!Fr_carray.allocate})
- [size res = max (size_p_i)]
- [size poly_polylen_coeff = nb_polys]
- [size p_i = size_p_i] *)externallinear:fr_array->(fr_array*int*fr)array->int->unit="caml_bls12_381_polynomial_polynomial_linear_stubs"[@@noalloc](** [linear_with_powers res c poly_polylen nb_polys] writes the result of
computing [c⁰·p₀(x) + c¹·p₁(x) + … + cᵏ·pₖ(x)] in [res], where
- [poly_polylen.[i] = (pᵢ, size_p_i)]
- [nb_polys] is a number of polynomials
requires:
- the result must be initialized with zero (as done by {!Fr_carray.allocate})
- [size res = max (size_p_i)]
- [size poly_polylen = nb_polys]
- [size p_i = size_p_i] *)externallinear_with_powers:fr_array->fr->(fr_array*int)array->int->unit="caml_bls12_381_polynomial_polynomial_linear_with_powers_stubs"[@@noalloc](** [negate res p n] writes the result of negating a polynomial [p] in [res]
requires:
- [size p = n]
- [size res = n]
- [res] and [p] either disjoint or equal *)externalnegate:fr_array->fr_array->int->unit="caml_bls12_381_polynomial_polynomial_negate_stubs"[@@noalloc](** [evaluate res p n x] writes the result of evaluating a polynomial [p] at [x]
in [res]
- requires: [size p = n] and [n > 0] *)externalevaluate:fr->fr_array->int->fr->unit="caml_bls12_381_polynomial_polynomial_evaluate_stubs"[@@noalloc](** [division_xn res_q res_r p size_p (n, c)] writes the quotient and remainder of
the division of a polynomial [p] by [(X^n + c)] in [res]
requires:
- [size p = size_p] and [size_p > n]
- [size res_q = size_p - n]
- [size res_r = n] *)externaldivision_xn:fr_array->fr_array->fr_array->int->int*fr->unit="caml_bls12_381_polynomial_polynomial_division_xn_stubs"[@@noalloc](** [mul_xn res p size_p n c] writes the result of multiplying a polynomial [p]
by [(X^n + c)] in [res]
requires:
- [res] is initialized with bls-fr zeros
- [size p = size_p]
- [size res = size_p + n] *)externalmul_xn:fr_array->fr_array->int->int->fr->unit="caml_bls12_381_polynomial_polynomial_mul_xn_stubs"[@@noalloc]externalderivative:fr_array->fr_array->int->unit="caml_bls12_381_polynomial_polynomial_derivative_stubs"[@@noalloc]endmodulePolynomial_impl=structtypescalar=Fr.ttypet=Fr_carray.t[@@derivingrepr]letof_carrayp=pletto_carrayp=pletlength=Fr_carray.lengthleterasep=Fr_carray.erasep(lengthp)letallocate=Fr_carray.allocateletcopyp=Fr_carray.copy~offset:0~len:(lengthp)pletcopy_carray=Fr_carray.copyletget=Fr_carray.getletdegree=Fr_carray.degreeletinit=Fr_carray.initletequalp1p2=letn1=lengthp1inletn2=lengthp2inletshort_n,long_p,long_n=ifn1<=n2then(n1,p2,n2)else(n2,p1,n1)inifFr_carray.equalp1~offset1:0p2~offset2:0~len:short_nthenletrecstop_at_first_non_zeroi=ifi=long_nthentrueelseifFr.eq(getlong_pi)Fr.zerothenstop_at_first_non_zero(i+1)elsefalseinstop_at_first_non_zeroshort_nelsefalseletto_stringp=String.concat" ; "(List.mapFr.to_string(Array.to_list@@Fr_carray.to_arrayp))(* ?of_sparse_coefficients *)letof_coefficientscoefficients=letcoefficients=Array.of_listcoefficientsinletdegree=Array.fold_left(funmax_degree(_coeff,d)->assert(d>=0);maxdmax_degree)0coefficientsinletpolynomial=allocate(degree+1)inStubs.of_sparsepolynomialcoefficients(Array.lengthcoefficients);polynomialletof_dense=Fr_carray.of_arrayletzero=of_coefficients[]letone=of_coefficients[(Fr.one,0)]letgenerate_biased_random_polynomialn=assert(n>=0);ifRandom.int10=0||n=0thenzeroelseletpoly=Array.initn(fun_->ifRandom.bool()thenFr.random()elseFr.copyFr.zero)inArray.setpoly(n-1)Fr.one;Fr_carray.of_arraypolyletrandomn=List.initn(funi->(Fr.random(),i))|>of_coefficientsletto_dense_coefficientsp=(* the polynomial could be padded with zero, so we instead of using [n] and
wasting some space we recompute the size of the minimal representation *)letlen=1+max0(degreep)inFr_carray.to_array~lenp(* ensures: no coefficient in the result is zero *)letto_sparse_coefficientspoly=letpoly=to_dense_coefficientspolyinletres=ref[]infordeg=Array.lengthpoly-1downto0doletcoef=poly.(deg)inifnot(Fr.is_zerocoef)thenres:=(Fr.copycoef,deg)::!resdone;!resletaddp1p2=letn1=lengthp1inletn2=lengthp2inletres_size=maxn1n2inletres=allocateres_sizeinStubs.addresp1p2n1n2;resletadd_inplaceresp1p2=letn1=lengthp1inletn2=lengthp2inletn_res=lengthresinassert(n_res=maxn1n2);Stubs.addresp1p2n1n2letsubp1p2=letn1=lengthp1inletn2=lengthp2inletmax_size=maxn1n2inletres=allocatemax_sizeinStubs.subresp1p2n1n2;resletsub_inplaceresp1p2=letn1=lengthp1inletn2=lengthp2inletn_res=lengthresinassert(n_res>=maxn1n2);Stubs.subresp1p2n1n2letmulp1p2=letn1=lengthp1inletn2=lengthp2inletres_size=n1+n2-1inletres=allocateres_sizeinStubs.mulresp1p2n1n2;resletmul_by_scalarscalarp=letn=lengthpinletres=allocateninStubs.mul_by_scalarresscalarpn;resletmul_by_scalar_inplaceresscalarp=letn=lengthpinletn_res=lengthresinassert(n_res>=n);Stubs.mul_by_scalarresscalarpnletlinearpolyscoeffs=letnb_polys=List.lengthpolysinassert(List.compare_length_withcoeffsnb_polys=0);letres_size=List.fold_left(funres_sizep->max(lengthp)res_size)0polysinifres_size=0thenzeroelseletres=allocateres_sizeinletpoly_polylen_coeff=List.map2(funpcoeff->(p,lengthp,coeff))polyscoeffsinStubs.linearres(Array.of_listpoly_polylen_coeff)nb_polys;resletlinear_with_powerspolyscoeff=letnb_polys=List.lengthpolysinletpolys=List.map(funp->(p,lengthp))polysinletres_size=List.fold_left(funres_size(_p,size)->maxsizeres_size)0polysinletres=allocateres_sizeinStubs.linear_with_powersrescoeff(Array.of_listpolys)nb_polys;resletoppositep=letn=lengthpinletres=allocateninStubs.negaterespn;resletopposite_inplacep=letn=lengthpinStubs.negateppnletis_zerop=ifdegreep=-1thentrueelsefalselettruncate~lenp=iflen<0thenraise(Invalid_argument"truncate: expected positive length.")else(* [min_len_capacity p] returns the minimum of [len] and [degree p + 1].
Here, [degree p + 1] is the minimal length of the {!type:Carray.t}
representing the polynomial [p].
When [p] is the zero polynomial its degree is -1, so we return 1 for
one coefficient holding the value. *)letmin_len_capacityp=ifis_zeropthen1elseminlen(degreep+1)inFr_carray.copy~len:(min_len_capacityp)pletevaluatepscalar=letn=lengthpinletres=Fr.copyscalarinStubs.evaluaterespnscalar;resexceptionRest_not_nullofstringletdivision_xnpnc=assert(n>0);letpoly_degree=degreepinletpoly_size=poly_degree+1inifpoly_degree=-1||poly_degree<nthen(zero,p)elseletres_q=allocate(poly_size-n)inletres_r=allocateninStubs.division_xnres_qres_rppoly_size(n,c);letpoly_q=res_qinletpoly_r=res_rin(poly_q,poly_r)letmul_xnpnc=letl=lengthpinletres=allocate(l+n)inStubs.mul_xnresplnc;resletderivativep=letn=lengthpinifis_zerop||n=1thenzeroelseletres=allocate(n-1)inStubs.derivativerespn;res(* for p polynomial, returns p splitted in nb_chunks parts of degree size_chunks ; the nb_chunks - 1 first parts have degree size_chunks or less (if it’s less the next parts are 0) ; the last part’s degree will contain the rest of p coefficients without any degree bound *)letsplit~nb_chunkssize_chunksp=letpoly_degree=degreepinletnb_coeff_P=1+poly_degreeinifpoly_degree=-1thenList.initnb_chunks(fun_->zero)elseList.initnb_chunks(funi->if(i+1)*size_chunks<=nb_coeff_Pthenifi=nb_chunks-1thenFr_carray.copy~offset:(i*size_chunks)pelseFr_carray.copy~offset:(i*size_chunks)~len:size_chunkspelseifi*size_chunks<nb_coeff_PthenFr_carray.copy~offset:(i*size_chunks)~len:(nb_coeff_P-(i*size_chunks))pelsezero)letblind~nb_blindsnp=letblinding_factor=randomnb_blindsin(addp(mul_xnblinding_factornFr.(negateone)),blinding_factor)let(=)=equallet(+)=addlet(-)=sublet(*)=mulletconstantc=of_coefficients[(c,0)]letfold_left_map=Fr_carray.fold_left_mapendmoduletypePolynomial_sig=sig(**
This library implements polynomials of Bls12_381.Fr as arrays of contiguous
memory in C, allowing much better performances for algorithms that scan the
polynomials.
An array [a] of size [n] represents the polynomial $\sum_i^(n-1) a[i] X^i$
The length of [a] is always greater or equal than the degree+1 of its
corresponding polynomial, if greater it padded with zeros. As a consequence a
polynomial has many representations, namely all arrays with trailing zeros.
*)typescalartypet[@@derivingrepr](** [init n f] returns a fresh polynomial of length [n], with element number [i]
initialized to the result of [f i]. *)valinit:int->(int->scalar)->t(** [allocate len] creates a zero polynomial of size [len] *)valallocate:int->t(** [erase p] overwrites a polynomial [p] with a zero polynomial of
the same size as the polynomial [p] *)valerase:t->unit(** [generate_biased_random_polynomial n] generates a random polynomial of
degree strictly lower than [n], the distribution is NOT uniform, it is
biased towards sparse polynomials and particularly towards the zero
polynomial *)valgenerate_biased_random_polynomial:int->t(** [random n] generates a uniformly sampled polynomial among the set of all
polynomials of degree strictly lower than [n] *)valrandom:int->t(** [degree p] returns the degree of a polynomial [p]. Returns [-1] for the
zero polynomial *)valdegree:t->int(** [get p i] returns the [i]-th element of a given array [p], a coefficient of [X^i]
in [p] *)valget:t->int->scalar(** [to_string p] returns the string representation of a polynomial [p] *)valto_string:t->string(** [copy p] returns a copy of a polynomial [p] *)valcopy:t->t(** [truncate ~len p] returns a new polynomial made of the first [len]
coefficients of [p]. If [degree p + 1] is less than [len] then
[copy p] is returned.
@raise [Invalid_argument] if [len] is negative. *)valtruncate:len:int->t->t(** [to_dense_coefficients p] returns the dense representation of
a polynomial [p], i.e., it converts a C array to an OCaml array *)valto_dense_coefficients:t->scalararray(** [of_dense p] creates a value of type [t] from the dense representation of
a polynomial [p], i.e., it converts an OCaml array to a C array *)valof_dense:scalararray->t(** [of_coefficients p] creates a value of type [t] from the sparse representation of
a polynomial [p], i.e., it converts an OCaml array to a C array *)valof_coefficients:(scalar*int)list->t(** [equal a b] checks whether a polynomial [a] is equal to a polynomial [b] *)valequal:t->t->bool(** [is_zero p] checks whether a polynomial [p] is the zero polynomial *)valis_zero:t->bool(** [zero] is the zero polynomial, the neutral element for polynomial addition *)valzero:t(** [one] is the constant polynomial one, the neutral element for polynomial
multiplication *)valone:t(** [add] computes polynomial addition *)valadd:t->t->t(** [add_inplace res a b] computes polynomial addition of [a] and [b] and
writes the result in [res]
Note: [res] can be equal to either [a] or [b] *)valadd_inplace:t->t->t->unit(** [sub] computes polynomial subtraction *)valsub:t->t->t(** [sub_inplace res a b] computes polynomial subtraction of [a] and [b] and
writes the result in [res]
Note: [res] can be equal to either [a] or [b] *)valsub_inplace:t->t->t->unit(** [mul] computes polynomial multiplication
Note: naive quadratic algorithm, result's size is the sum of arguments' size *)valmul:t->t->t(** [mul_by_scalar] computes multiplication of a polynomial by a blst_fr element *)valmul_by_scalar:scalar->t->t(** [mul_by_scalar_inplace res s p] computes multiplication of a polynomial [p]
by a blst_fr element [s] and stores it in [res] *)valmul_by_scalar_inplace:t->scalar->t->unit(** [linear p s] computes [∑ᵢ s.(i)·p.(i)] *)vallinear:tlist->scalarlist->t(** [linear_with_powers p s] computes [∑ᵢ sⁱ·p.(i)]. This function is more efficient
than [linear] + [powers] *)vallinear_with_powers:tlist->scalar->t(** [opposite] computes polynomial negation *)valopposite:t->t(** [opposite_inplace p] computes polynomial negation
Note: The argument [p] is overwritten *)valopposite_inplace:t->unit(** [evaluate p x] evaluates a polynomial [p] at [x] *)valevaluate:t->scalar->scalarexceptionRest_not_nullofstring(** [division_xn p n c] returns the quotient and remainder of the division of
[p] by [(X^n + c)] *)valdivision_xn:t->int->scalar->t*t(** [mul_xn p n c] returns the product of [p] and [(X^n + c)] *)valmul_xn:t->int->scalar->t(** [derivative p] returns the formal derivative of [p] *)valderivative:t->tvalsplit:nb_chunks:int->int->t->tlist(** [blind ~nb_blinds n p] adds to polynomial [p] a random multiple of
polynomial [(X^n - 1)], chosen by uniformly sampling a polynomial [b]
of degree strictly lower than [nb_blinds] and multiplying it by
[(X^n - 1)], [b] is returned as the second argument *)valblind:nb_blinds:int->int->t->t*t(** Infix operator for {!equal} *)val(=):t->t->bool(** Infix operator for {!add} *)val(+):t->t->t(** Infix operator for {!sub} *)val(-):t->t->t(** Infix operator for {!mul} *)val(*):t->t->t(** [constant s] creates a value of type [t] from a blst_fr element [s] *)valconstant:scalar->t(** [fold_left_map] is a combination of fold_left and map that threads an
accumulator through calls to [f]. *)valfold_left_map:('acc->scalar->'acc*scalar)->'acc->t->'acc*tendmoduletypePolynomial_unsafe_sig=sigincludePolynomial_sigwithtypet=Fr_carray.t(** [to_carray p] converts [p] from type {!type:t} to type {!type:Fr_carray.t}
Note: [to_carray p] doesn't create a copy of [p] *)valto_carray:t->Fr_carray.t(** [of_carray p] converts [p] from type {!type:Fr_carray.t} to type {!type:t}
Note: [of_carray p] doesn't create a copy of [p] *)valof_carray:Fr_carray.t->t(** [copy_carray ?offset ?len p] returns a polynomial made of [len] contiguous
coefficients starting from the coefficient of index [offset].
By default, [offset = 0] and [len = length p - offset].
@raise [Invalid_argument] if [offset] is not in the range 0 to [(length p - 1)],
or if [len] is not positive, or if [offset + length] is not in the range 0 to
[(length p - 1)]. *)valcopy_carray:?offset:int->?len:int->t->t(** [length p] returns the length of the underlying {!type:Fr_carray.t}. *)vallength:t->intvalto_sparse_coefficients:t->(scalar*int)listendmodulePolynomial_unsafe:Polynomial_unsafe_sigwithtypescalar=Bls12_381.Fr.t=Polynomial_implinclude(Polynomial_unsafe:Polynomial_sigwithtypescalar=Polynomial_unsafe.scalarandtypet=Polynomial_unsafe.t)