1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105(*****************************************************************************)(* *)(* 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. *)(* *)(*****************************************************************************)(** Plompiler standard library. *)openLang_coremoduletypeLimb_list=sig(** Input for a Plompiler program. *)type'ainput(** Element of the native scalar field. *)typescalar(** Representation of values. *)type'arepr(** Plompiler program. *)type'at(** Representation of elements. *)typetl(** [input_bytes ~le b] returns the representation of [b]
that Plompiler expects as input.
[le] can be used to set the endianness. *)valinput_bytes:le:bool->bytes->tlinput(** [constant ~le b] returns the constant [b] as a Plompiler value.
[le] can be used to set the endianness. *)valconstant:le:bool->bytes->tlreprt(** [of_scalar ~total_nb_bits b] converts the scalar [b] of size
[total_nb_bits] in bits into the [tl] representation. *)valof_scalar:total_nb_bits:int->scalarrepr->tlreprt(** [to_scalar b] return the scalar representing the value [b]. *)valto_scalar:tlrepr->scalarreprt(** [of_bool_list b] converts the list of bits in little-endian
order into the [tl] representation. *)valof_bool_list:boollistrepr->tlreprt(** [to_bool_list b] returns the list of bits in little-endian
order, representing the value [b]. *)valto_bool_list:tlrepr->boollistreprt(** [xor a b] returns the exclusive disjunction of [a] and [b]. *)valxor:tlrepr->tlrepr->tlreprt(** [band a b] returns the conjunction of [a] and [b]. *)valband:tlrepr->tlrepr->tlreprt(** [not b] returns the negation of [b]. *)valnot:tlrepr->tlreprt(** [rotate_right b n] shifts the bits right by n positions,
so that each bit is less significant.
The least significant bit becomes the most significant
i.e. it is "rotated".
[rotate_right bs (length bl) = bl] *)valrotate_right:tlrepr->int->tlreprt(** [shift_right b n] shifts all bits right by n positions,
so that each bit is less significant.
The least signigicant bit is lost and the most significant bit is
set to zero.
More precisely, if we interpret the [b] as an integer,
[shift_right b n = b / 2^n] *)valshift_right:tlrepr->int->tlreprtend(** The {!LIB} module type extends the core language defined in {!Lang_core.COMMON}
by adding functions that build upon those primitives.
*)moduletypeLIB=sigincludeCOMMON(** [foldiM] is the monadic version of a fold over a natural number. *)valfoldiM:('a->int->'at)->'a->int->'at(** [fold2M] is the monadic version of [List.fold_left2]. *)valfold2M:('a->'b->'c->'at)->'a->'blist->'clist->'at(** [mapM] is the monadic version of [List.map]. *)valmapM:('a->'bt)->'alist->'blistt(** [map2M] is the monadic version of [List.map2]. *)valmap2M:('a->'b->'ct)->'alist->'blist->'clistt(** [iterM] is the monadic version of [List.iter]. *)valiterM:('a->unitreprt)->'alist->unitreprt(** [iter2M] is the monadic version of [List.iter2]. *)valiter2M:('a->'b->unitreprt)->'alist->'blist->unitreprtmoduleBool:sigincludeBOOL(** Returns the pair (s, c_out), as per
https://en.wikipedia.org/wiki/Adder_(electronics)#Full_adder *)valfull_adder:boolrepr->boolrepr->boolrepr->(bool*bool)reprtendwithtypescalar=scalarandtype'arepr='areprandtype'at='atmoduleNum:sigincludeNUM(** [square a] returns the value [a^2]. *)valsquare:scalarrepr->scalarreprt(** [pow b e_bits] returns the value [b^e] where [e] is the
number represented by the binary decomposition [e_bits]. *)valpow:scalarrepr->boolreprlist->scalarreprt(** [add_list ~qc ~coeffs l] returns the sum of the elements of [l]
weighted by [coeffs].
Note: if [coeffs] is defined, it should be of the same length
as [l].
*)valadd_list:?qc:S.t->?coeffs:S.tlist->scalarlistrepr->scalarreprt(** [mul_list l] returns the product of the elements of [l]. *)valmul_list:scalarlistrepr->scalarreprt(** [mul_by_constant k a] returns the value [k * a]. *)valmul_by_constant:S.t->scalarrepr->scalarreprt(* [scalar_of_bytes bs] returns the scalar represented by the binary
sequence [bs].
Specifically, it evaluates P(X) = \sum_i bᵢ Xⁱ at 2. *)valscalar_of_bytes:boollistrepr->scalarreprt(** [is_eq_const a k] returns whether [a] is equal to [k]. *)valis_eq_const:scalarrepr->S.t->boolreprt(** [assert_eq_const a k] asserts that [a] is equal to [k]. *)valassert_eq_const:scalarrepr->S.t->unitreprt(** [is_upper_bounded ~bound x] returns whether the scalar [x] is
strictly lower than [bound] when [x] is interpreted as an integer
from [0] to [p-1] (being [p] the scalar field order).
This circuit is total (and more expensive than our version below). *)valis_upper_bounded:bound:Z.t->scalarrepr->boolreprt(** Same as [is_upper_bounded] but cheaper and partial.
[is_upper_bounded_unsafe ~bound l] is unsatisfiable if l cannot be
represented in binary with [Z.numbits bound] bits. *)valis_upper_bounded_unsafe:?nb_bits:int->bound:Z.t->scalarrepr->boolreprt(** [geq (a, bound_a) (b, bound_b)] returns the boolean wire representing
a >= b.
Pre-condition: [a ∈ \[0, bound_a) ∧ b ∈ \[0, bound_b)] *)valgeq:scalarrepr*Z.t->scalarrepr*Z.t->boolreprtendwithtypescalar=scalarandtype'arepr='areprandtype'at='at(** Enumerations, represented as a list of cases. *)moduleEnum(N:sigvaln:intend):sig(** [switch_case k l] returns the k-th element of the list [l] if
k ∈ \[0,n) or the first element of [l] otherwise. *)valswitch_case:scalarrepr->'alistrepr->'areprtend(** Representation of bytes. *)moduleBytes:sig(** Little-endian representation of bytes.
First element of the list is the Least Significant Bit. *)includeLimb_listwithtypetl=boollistandtypescalar=scalarandtype'arepr='areprandtype'at='atandtype'ainput='aInput.t(** [constant_uint32 ~le n] returns a value holding the bytes correspoding
to the uint [n].
[le] can be used to set the endianness. *)valconstant_uint32:le:bool->Stdint.uint32->tlreprt(** [length b] returns the length of [b] in bits. *)vallength:tlrepr->int(** [concat bs] returns the concatenation of the bitlists in [bs]. *)valconcat:tlreprarray->tlrepr(** [add b1 b2] computes the addition of [b1] and [b2]. *)valadd:?ignore_carry:bool->tlrepr->tlrepr->tlreprt(** [rotate_left bl n] shifts the bits left by n positions,
so that each bit is more significant.
The most significant bit becomes the least significant
i.e. it is "rotated".
[rotate_left bl (length bl) = bl] *)valrotate_left:tlrepr->int->tlreprt(** [shift_left bl n] shifts all bits left by n positions,
so that each bit is more significant.
The most signigicant bit is lost and the least significant bit is
set to zero.
More precisely, if we interpret the [bl] as an integer
[shift_left bl i = bl * 2^i mod 2^{length a}] *)valshift_left:tlrepr->int->tlreprtend(** This module is a more generic version of Bytes, where each scalar
stores an [nb_bits]-bit number. *)moduleLimbs(N:sigvalnb_bits:intend):sigincludeLimb_listwithtypetl=scalarlistandtypescalar=scalarandtype'arepr='areprandtype'at='atandtype'ainput='aInput.tend(** [add2 p1 p2] returns the pair [(fst p1 + fst p2, snd p1 + snd p2)]. *)valadd2:(scalar*scalar)repr->(scalar*scalar)repr->(scalar*scalar)reprtmoduleEncodings:sig(**
Encoding type for encapsulating encoding/decoding/input functions.
This type enables us to use more structured types for data
in circuits.
For that, encoding is parameterized by 3 types:
- 'oh is the type of the high-level OCaml representation
- 'u is the unpacked type, i.e. a collection of atomic reprs.
- `p is the packed type, i.e the inner type of Plompiler's repr.
For example, for the representation of a point (pair of scalars),
one might have:
{[
( {x:int; y:int},
scalar repr * scalar repr,
scalar * scalar
) encoding
]}
The first type, the record [{x:int; y:int}], represents an OCaml point,
which becomes the argument taken by the [input] function.
The second type, [scalar repr * scalar repr], is an unpacking of the
encoding. This is used for the result of [decode]. We can use any type
isomorphic to [scalar repr * scalar repr] here.
The last type must be [scalar * scalar], as an encoding of a point will be
of the type [(scalar * scalar) repr].
*)type('oh,'u,'p)encoding={encode:'u->'prepr;decode:'prepr->'u;input:'oh->'pInput.input;of_input:'pInput.input->'oh;}(**
The function [conv] defines conversions for [encoding]s, by changing
the higher-level ['u] and and ['oh] types.
*)valconv:('u1->'u0)->('u0->'u1)->('o1->'o0)->('o0->'o1)->('o0,'u0,'p)encoding->('o1,'u1,'p)encodingvalwith_implicit_bool_check:('prepr->boolreprt)->('o,'u,'p)encoding->('o,'u,'p)encodingvalwith_assertion:('prepr->unitreprt)->('o,'u,'p)encoding->('o,'u,'p)encodingvalscalar_encoding:(Bls12_381.Fr.t,scalarrepr,scalar)encodingvalbool_encoding:(bool,boolrepr,bool)encodingvallist_encoding:('a,'b,'c)encoding->('alist,'blist,'clist)encodingvalatomic_list_encoding:('a,'brepr,'c)encoding->('alist,'blistrepr,'clist)encodingvalobj2_encoding:('a,'b,'c)encoding->('d,'e,'f)encoding->('a*'d,'b*'e,'c*'f)encodingvalatomic_obj2_encoding:('a,'brepr,'c)encoding->('d,'erepr,'f)encoding->('a*'d,('b*'e)repr,'c*'f)encodingvalobj3_encoding:('a,'b,'c)encoding->('d,'e,'f)encoding->('g,'h,'i)encoding->('a*('d*'g),'b*('e*'h),'c*('f*'i))encodingvalatomic_obj3_encoding:('a,'brepr,'c)encoding->('d,'erepr,'f)encoding->('g,'hrepr,'i)encoding->('a*('d*'g),('b*('e*'h))repr,'c*('f*'i))encodingvalobj4_encoding:('a,'b,'c)encoding->('d,'e,'f)encoding->('g,'h,'i)encoding->('j,'k,'l)encoding->('a*('d*('g*'j)),'b*('e*('h*'k)),'c*('f*('i*'l)))encodingvalatomic_obj4_encoding:('a,'brepr,'c)encoding->('d,'erepr,'f)encoding->('g,'hrepr,'i)encoding->('j,'krepr,'l)encoding->('a*('d*('g*'j)),('b*('e*('h*'k)))repr,'c*('f*('i*'l)))encodingvalobj5_encoding:('a,'b,'c)encoding->('d,'e,'f)encoding->('g,'h,'i)encoding->('j,'k,'l)encoding->('m,'n,'o)encoding->('a*('d*('g*('j*'m))),'b*('e*('h*('k*'n))),'c*('f*('i*('l*'o))))encodingvalatomic_obj5_encoding:('a,'brepr,'c)encoding->('d,'erepr,'f)encoding->('g,'hrepr,'i)encoding->('j,'krepr,'l)encoding->('m,'nrepr,'o)encoding->('a*('d*('g*('j*'m))),('b*('e*('h*('k*'n))))repr,'c*('f*('i*('l*'o))))encodingvalobj6_encoding:('a,'b,'c)encoding->('d,'e,'f)encoding->('g,'h,'i)encoding->('j,'k,'l)encoding->('m,'n,'o)encoding->('p,'q,'r)encoding->('a*('d*('g*('j*('m*'p)))),'b*('e*('h*('k*('n*'q)))),'c*('f*('i*('l*('o*'r)))))encodingvalatomic_obj6_encoding:('a,'brepr,'c)encoding->('d,'erepr,'f)encoding->('g,'hrepr,'i)encoding->('j,'krepr,'l)encoding->('m,'nrepr,'o)encoding->('p,'qrepr,'r)encoding->('a*('d*('g*('j*('m*'p)))),('b*('e*('h*('k*('n*'q)))))repr,'c*('f*('i*('l*('o*'r)))))encodingvalobj7_encoding:('a,'b,'c)encoding->('d,'e,'f)encoding->('g,'h,'i)encoding->('j,'k,'l)encoding->('m,'n,'o)encoding->('p,'q,'r)encoding->('s,'t,'u)encoding->('a*('d*('g*('j*('m*('p*'s))))),'b*('e*('h*('k*('n*('q*'t))))),'c*('f*('i*('l*('o*('r*'u))))))encodingvalatomic_obj7_encoding:('a,'brepr,'c)encoding->('d,'erepr,'f)encoding->('g,'hrepr,'i)encoding->('j,'krepr,'l)encoding->('m,'nrepr,'o)encoding->('p,'qrepr,'r)encoding->('s,'trepr,'u)encoding->('a*('d*('g*('j*('m*('p*'s))))),('b*('e*('h*('k*('n*('q*'t))))))repr,'c*('f*('i*('l*('o*('r*'u))))))encodingvalobj8_encoding:('a,'b,'c)encoding->('d,'e,'f)encoding->('g,'h,'i)encoding->('j,'k,'l)encoding->('m,'n,'o)encoding->('p,'q,'r)encoding->('s,'t,'u)encoding->('v,'w,'x)encoding->('a*('d*('g*('j*('m*('p*('s*'v)))))),'b*('e*('h*('k*('n*('q*('t*'w)))))),'c*('f*('i*('l*('o*('r*('u*'x)))))))encodingvalatomic_obj8_encoding:('a,'brepr,'c)encoding->('d,'erepr,'f)encoding->('g,'hrepr,'i)encoding->('j,'krepr,'l)encoding->('m,'nrepr,'o)encoding->('p,'qrepr,'r)encoding->('s,'trepr,'u)encoding->('v,'wrepr,'x)encoding->('a*('d*('g*('j*('m*('p*('s*'v)))))),('b*('e*('h*('k*('n*('q*('t*'w)))))))repr,'c*('f*('i*('l*('o*('r*('u*'x)))))))encodingvalobj9_encoding:('a,'b,'c)encoding->('d,'e,'f)encoding->('g,'h,'i)encoding->('j,'k,'l)encoding->('m,'n,'o)encoding->('p,'q,'r)encoding->('s,'t,'u)encoding->('v,'w,'x)encoding->('y,'z,'a1)encoding->('a*('d*('g*('j*('m*('p*('s*('v*'y))))))),'b*('e*('h*('k*('n*('q*('t*('w*'z))))))),'c*('f*('i*('l*('o*('r*('u*('x*'a1))))))))encodingvalatomic_obj9_encoding:('a,'brepr,'c)encoding->('d,'erepr,'f)encoding->('g,'hrepr,'i)encoding->('j,'krepr,'l)encoding->('m,'nrepr,'o)encoding->('p,'qrepr,'r)encoding->('s,'trepr,'u)encoding->('v,'wrepr,'x)encoding->('y,'zrepr,'a1)encoding->('a*('d*('g*('j*('m*('p*('s*('v*'y))))))),('b*('e*('h*('k*('n*('q*('t*('w*'z))))))))repr,'c*('f*('i*('l*('o*('r*('u*('x*'a1))))))))encodingvalobj10_encoding:('a,'b,'c)encoding->('d,'e,'f)encoding->('g,'h,'i)encoding->('j,'k,'l)encoding->('m,'n,'o)encoding->('p,'q,'r)encoding->('s,'t,'u)encoding->('v,'w,'x)encoding->('y,'z,'a1)encoding->('b1,'c1,'d1)encoding->('a*('d*('g*('j*('m*('p*('s*('v*('y*'b1)))))))),'b*('e*('h*('k*('n*('q*('t*('w*('z*'c1)))))))),'c*('f*('i*('l*('o*('r*('u*('x*('a1*'d1)))))))))encodingvalatomic_obj10_encoding:('a,'brepr,'c)encoding->('d,'erepr,'f)encoding->('g,'hrepr,'i)encoding->('j,'krepr,'l)encoding->('m,'nrepr,'o)encoding->('p,'qrepr,'r)encoding->('s,'trepr,'u)encoding->('v,'wrepr,'x)encoding->('y,'zrepr,'a1)encoding->('b1,'c1repr,'d1)encoding->('a*('d*('g*('j*('m*('p*('s*('v*('y*'b1)))))))),('b*('e*('h*('k*('n*('q*('t*('w*('z*'c1)))))))))repr,'c*('f*('i*('l*('o*('r*('u*('x*('a1*'d1)))))))))encodingendendmoduleLib(C:COMMON)=structincludeCletfoldiM:('a->int->'at)->'a->int->'at=funfen->foldMfe(List.initn(funi->i))letfold2Mfacclsrs=foldM(funacc(l,r)->facclr)acc(List.combinelsrs)letmapMfl=let*l=foldM(funacce->let*e=feinret@@(e::acc))[]linret@@List.revlletmap2Mflsrs=mapM(fun(l,r)->flr)(List.combinelsrs)letiterMfl=foldM(fun_a->fa)unitlletiter2Mflr=iterM(fun(l,r)->flr)(List.combinelr)moduleBool=structincludeBoolletfull_adderabc_in=with_label~label:"Bool.full_adder"@@let*a_xor_b=xorabinlet*a_xor_b_xor_c=xora_xor_bc_ininlet*a_xor_b_and_c=banda_xor_bc_ininlet*a_and_b=bandabinlet*c=bora_xor_b_and_ca_and_binret(paira_xor_b_xor_cc)endmoduleNum=structincludeNumletsquarel=mulllletpowxn_list=let*init=let*left=Num.oneinret(left,x)inlet*res,_acc=foldM(fun(res,acc)bool->let*res_true=mulresaccinlet*res=Bool.ifthenelseboolres_trueresinlet*acc=mulaccaccinret(res,acc))initn_listinretresletadd_list?(qc=S.zero)?(coeffs=[])l=letl=of_listlinletq=ifcoeffs!=[]thencoeffselseList.init(List.lengthl)(fun_->S.one)inassert(List.compare_lengthsql=0);match(l,q)with|x1::x2::xs,ql::qr::qs->let*res=Num.add~qc~ql~qrx1x2infold2M(funaccxql->Num.add~qlxacc)resxsqs|[x],[ql]->Num.add_constant~qlqcx|[],[]->Num.constantqc|_,_->assertfalseletmul_listl=matchof_listlwith[]->assertfalse|x::xs->foldMNum.mulxxsletmul_by_constantsx=Num.add_constant~ql:sS.zeroxletscalar_of_bytesb=letsb=List.mapscalar_of_bool(of_listb)inscalar_of_limbs~nb_bits:1(to_listsb)letassert_eq_constls=Num.assert_custom~ql:S.mone~qc:slllletis_eq_constls=let*diff=add_constant~ql:S.moneslinis_zerodiff(* Function used by [is_upper_bounded(_unsafe)] *)letignore_leading_zeros~nb_bits~boundxbits=(* We can ignore all leading zeros in the bound's little-endian binary
decomposition. The assertion cannot be satisfied if they are all zeros. *)letrecshave_zeros=function|[]->raise(Invalid_argument"is_upper_bounded cannot be satisfied on bound = 0")|(x,true)::tl->(x,tl)|(_,false)::tl->shave_zerostlinList.combine(of_listxbits)(Utils.bool_list_of_z~nb_bitsbound)|>shave_zeros(* Let [(bn,...,b0)] and [(xn,...,x0)] be binary representations
of [bound] and [x] respectively where the least significant bit is
indexed by [0]. Let [op_i = if b_i = 1 then band else bor] for all [i].
Predicate [x] < [bound] can be expressed as the negation of predicate:
[ xn op_n (... (x1 op_1 (x0 op_0 true))) ].
Intuitively we need to carry through a flag that indicates if up to
step i, x is greater than b. In order for x[0,i] to be greater than
b[0,i]:
- if b_i = one then x_i will need to match it and the flag must be true.
- if b_i = zero then x needs to be one if the flag is false or it can
have any value if the flag is already true. *)letis_upper_bounded_unsafe?nb_bits~boundx=assert(Z.zero<=bound&&bound<S.order);letnb_bits=Option.value~default:(ifZ.(equalboundzero)then1elseZ.numbitsbound)nb_bitsinlet*xbits=bits_of_scalar~nb_bitsxinletinit,xibi=ignore_leading_zeros~nb_bits~boundxbitsinlet*geq=foldM(funacc(xi,bi)->letop=ifbithenBool.bandelseBool.borinopxiacc)initxibiinBool.bnotgeqletis_upper_bounded~boundx=assert(Z.zero<=bound&&bound<S.order);letnb_bits=Z.numbitsS.orderinletbound_plus_alpha=Z.(bound+Utils.alpha)inlet*xbits=bits_of_scalar~shift:Utils.alpha~nb_bitsxinletinit,xibi=ignore_leading_zeros~nb_bits~bound:bound_plus_alphaxbitsinlet*geq=foldM(funacc(xi,bi)->letop=ifbithenBool.bandelseBool.borinopxiacc)initxibiinBool.bnotgeqletgeq(a,bound_a)(b,bound_b)=(* (a - b) + bound_b - 1 ∈ [0, bound_a + bound_b - 1) *)let*shifted_diff=Num.add~qr:S.mone~qc:(S.of_zZ.(predbound_b))abinletnb_bits=Z.(numbits@@pred(addbound_abound_b))inlet*bits=bits_of_scalar~nb_bitsshifted_diffinletinit,xibi=ignore_leading_zeros~nb_bits~bound:Z.(predbound_b)bitsinfoldM(funacc(xi,bi)->letop=ifbithenBool.bandelseBool.borinopxiacc)initxibiendmoduleEnum(N:sigvaln:intend)=structletswitch_casekcases=letcases=of_listcasesinassert(List.compare_length_withcasesN.n=0);letindexed=List.mapi(funix->(i,x))casesinfoldM(func(i,ci)->let*f=Num.is_eq_constk(S.of_z(Z.of_inti))inBool.ifthenelsefcic)(snd@@List.hdindexed)(List.tlindexed)endmoduleBytes=structtype'ainput='aInput.ttypenonrecscalar=scalartypenonrec'arepr='areprtypenonrec'at='at(* first element of the list is the Least Significant Bit *)typetl=boollistletinput_bitlistl=Input.list(List.mapInput.booll)letinput_bytes~leb=input_bitlist@@Utils.bitlist~lebletof_bool_listx=retxletto_bool_listx=retxletof_scalar~total_nb_bitsx=bits_of_scalar~nb_bits:total_nb_bitsxletto_scalar=Num.scalar_of_bytesletconstant~leb=letbl=Utils.bitlist~lebinlet*ws=foldM(funwsbit->let*w=Bool.constantbitinret(w::ws))[]blinret@@to_list@@List.revwsletconstant_uint32~leu32=letb=Stdlib.Bytes.create4inStdint.Uint32.to_bytes_big_endianu32b0;constant~lebletlengthb=List.length(of_listb)letconcat:tlreprarray->tlrepr=funbs->letbs=Array.to_listbsinletbs=List.revbsinletbs=List.mapof_listbsinletbs=List.concatbsinto_listbsletcheck_args_lengthnameab=letla=lengthainletlb=lengthbinifla!=lbthenraise(Invalid_argument(Format.sprintf"%s arguments of different lengths %i %i"namelalb))letadd?(ignore_carry=true)ab=check_args_length"Bytes.add"ab;with_label~label:"Bytes.add"@@letha,ta=(List.hd(of_lista),List.tl(of_lista))inlethb,tb=(List.hd(of_listb),List.tl(of_listb))inlet*a_xor_b=Bool.xorhahbinlet*a_and_b=Bool.bandhahbinlet*res,carry=fold2M(fun(res,c)ab->let*p=Bool.full_adderabcinlets,c=of_pairpinret(s::res,c))([a_xor_b],a_and_b)tatbinret@@to_list@@List.rev(ifignore_carrythenreselsecarry::res)letxorab=check_args_length"Bytes.xor"ab;let*l=map2MBool.xor(of_lista)(of_listb)inret@@to_listlletnota=let*l=mapMBool.bnot(of_lista)inret@@to_listlletbandab=check_args_length"Bytes.band"ab;let*l=map2MBool.band(of_lista)(of_listb)inret@@to_listlletrotate_rightai=letsplit_nnl=letrecauxacckl=ifk=nthen(List.revacc,l)elsematchlwith|h::t->aux(h::acc)(k+1)t|[]->raise(Invalid_argument(Printf.sprintf"split_n: n=%d >= List.length l=%d"nk))inaux[]0linlethead,tail=split_ni(of_lista)inret@@to_list@@tail@headletrotate_leftai=rotate_righta(lengtha-i)letshift_leftai=let*zero=Bool.constantfalseinletl=of_listainletlength=List.lengthl-iinassert(length>=0);letres=List.initi(fun_->zero)@List.filteri(funj_x->j<length)linret@@to_listresletshift_rightai=let*zero=Bool.constantfalseinletl=of_listainassert(List.compare_length_withli>=0);letres=List.filteri(funj_x->j>=i)l@List.initi(fun_->zero)inret@@to_listresendmoduleLimbs(N:sigvalnb_bits:intend)=structtype'ainput='aInput.ttypenonrecscalar=scalartypenonrec'arepr='areprtypenonrec'at='atmoduleLimbN=Limb(N)typetl=scalarlistletnb_bits=N.nb_bitsletinput_bytes~leb=letbl=Utils.bitlist~lebinletlimbs=Utils.limbs_of_bool_list~nb_bitsblinletr=List.mapS.of_intlimbsinInput.with_assertion(funx->iterM(Num.range_check~nb_bits)(of_listx))(Input.list(List.mapInput.scalarr))letof_bool_listx=letbl=Utils.split_exactly(of_listx)nb_bitsinlet*r=mapM(funx->Num.scalar_of_bytes(to_listx))blinret@@to_listrletto_bool_listl=let*lb=mapM(funz->let*bz=bits_of_scalar~nb_bitszinret@@of_listbz)(of_listl)inret@@to_list(List.concatlb)letto_scalarb=scalar_of_limbs~nb_bitsbletof_scalar~total_nb_bitsb=limbs_of_scalar~total_nb_bits~nb_bitsbletconstant~leb=letbl=Utils.bitlist~lebinletlimbs=Utils.limbs_of_bool_list~nb_bitsblinlet*r=mapM(funx->Num.constant@@S.of_intx)limbsinret@@to_listrletxorab=let*r=map2MLimbN.xor_lookup(of_lista)(of_listb)inret@@to_listrletbandab=let*r=map2MLimbN.band_lookup(of_lista)(of_listb)inret@@to_listrletnotb=let*r=mapMLimbN.bnot_lookup(of_listb)inret@@to_listrletrotate_or_shift_right_rem0~is_shiftaind=leta=Array.of_list(of_lista)inletlen=Array.lengthainlet*zero=Num.zeroinletr=List.init(len-ind)(funi->a.(i+ind))@List.initind(funi->ifis_shiftthenzeroelsea.(i))inret@@to_listrletrotate_or_shift_right_rem~is_shiftarem=assert(rem<nb_bits);leta=Array.of_list(of_lista)inletlen=Array.lengthainletget_ii:scalarreprt=ifi<len-1thenLimbN.rotate_right_lookupa.(i)a.(i+1)remelselet*zero=Num.zeroinleta0=ifis_shiftthenzeroelsea.(0)inLimbN.rotate_right_lookupa.(i)a0reminlet*r=mapMget_i(List.initlen(funi->i))inret@@to_listrletrotate_or_shift_right~is_shiftai=letind=i/nb_bitsinletrem=imodnb_bitsinlet*res=rotate_or_shift_right_rem0~is_shiftaindinifrem>0thenrotate_or_shift_right_rem~is_shiftresremelseretresletrotate_rightai=rotate_or_shift_right~is_shift:falseailetshift_rightai=rotate_or_shift_right~is_shift:trueaiendletadd2p1p2=letx1,y1=of_pairp1inletx2,y2=of_pairp2inlet*x3=Num.addx1x2inlet*y3=Num.addy1y2inret(pairx3y3)moduleEncodings=structtype('oh,'u,'p)encoding={encode:'u->'prepr;decode:'prepr->'u;input:'oh->'pInput.t;of_input:'pInput.t->'oh;}letconv:('u1->'u0)->('u0->'u1)->('o1->'o0)->('o0->'o1)->('o0,'u0,'p)encoding->('o1,'u1,'p)encoding=funfgfigie->letencodea=e.encode@@fainletdecodeb=g@@e.decodebinletinputx=e.input@@fixinletof_inputx=gi@@e.of_inputxin{encode;decode;input;of_input}letwith_implicit_bool_check:('prepr->boolreprt)->('o,'u,'p)encoding->('o,'u,'p)encoding=funbce->{ewithinput=(funx->Input.with_implicit_bool_checkbc@@e.inputx)}letwith_assertion:('prepr->unitreprt)->('o,'u,'p)encoding->('o,'u,'p)encoding=funassertione->{ewithinput=(funx->Input.with_assertionassertion@@e.inputx)}(** Encoding combinators *)letscalar_encoding=letencodex=xinletdecodex=xinletinput=Input.scalarinletof_input=Input.to_scalarin{encode;decode;input;of_input}letbool_encoding=letencodex=xinletdecodex=xinletinput=Input.boolinletof_input=Input.to_boolin{encode;decode;input;of_input}letlist_encoding(e:_encoding)=letencodea=to_list@@List.mape.encodeainletdecodex=List.mape.decode(of_listx)inletinputa=Input.list@@List.mape.inputainletof_inputx=List.mape.of_input(Input.to_listx)in{encode;decode;input;of_input}(* Encoding for lists, where we keep the repr of the list itself, not a list
of repr *)letatomic_list_encoding:('a,'brepr,'c)encoding->('alist,'blistrepr,'clist)encoding=fune->letencodea=to_list@@List.mape.encode(of_lista)inletdecodex=to_list@@List.mape.decode(of_listx)inletinputa=Input.list@@List.mape.inputainletof_inputx=List.mape.of_input(Input.to_listx)in{encode;decode;input;of_input}letobj2_encoding(el:_encoding)(er:_encoding)=letencode(a,b)=pair(el.encodea)(er.encodeb)inletdecodep=leta,b=of_pairpin(el.decodea,er.decodeb)inletinput(a,f)=Input.pair(el.inputa)(er.inputf)inletof_inputp=leta,b=Input.to_pairpin(el.of_inputa,er.of_inputb)in{encode;decode;input;of_input}letatomic_obj2_encoding:('a,'brepr,'c)encoding->('d,'erepr,'f)encoding->('a*'d,('b*'e)repr,'c*'f)encoding=funeler->letencodep=leta,b=of_pairpinpair(el.encodea)(er.encodeb)inletdecodep=leta,b=of_pairpinpair(el.decodea)(er.decodeb)inletinput(a,f)=Input.pair(el.inputa)(er.inputf)inletof_inputp=leta,b=Input.to_pairpin(el.of_inputa,er.of_inputb)in{encode;decode;input;of_input}letobj3_encodinge0e1e2=obj2_encodinge0(obj2_encodinge1e2)letatomic_obj3_encodinge0e1e2=atomic_obj2_encodinge0(atomic_obj2_encodinge1e2)letobj4_encodinge0e1e2e3=obj2_encodinge0(obj3_encodinge1e2e3)letatomic_obj4_encodinge0e1e2e3=atomic_obj2_encodinge0(atomic_obj3_encodinge1e2e3)letobj5_encodinge0e1e2e3e4=obj2_encodinge0(obj4_encodinge1e2e3e4)letatomic_obj5_encodinge0e1e2e3e4=atomic_obj2_encodinge0(atomic_obj4_encodinge1e2e3e4)letobj6_encodinge0e1e2e3e4e5=obj2_encodinge0(obj5_encodinge1e2e3e4e5)letatomic_obj6_encodinge0e1e2e3e4e5=atomic_obj2_encodinge0(atomic_obj5_encodinge1e2e3e4e5)letobj7_encodinge0e1e2e3e4e5e6=obj2_encodinge0(obj6_encodinge1e2e3e4e5e6)letatomic_obj7_encodinge0e1e2e3e4e5e6=atomic_obj2_encodinge0(atomic_obj6_encodinge1e2e3e4e5e6)letobj8_encodinge0e1e2e3e4e5e6e7=obj2_encodinge0(obj7_encodinge1e2e3e4e5e6e7)letatomic_obj8_encodinge0e1e2e3e4e5e6e7=atomic_obj2_encodinge0(atomic_obj7_encodinge1e2e3e4e5e6e7)letobj9_encodinge0e1e2e3e4e5e6e7e8=obj2_encodinge0(obj8_encodinge1e2e3e4e5e6e7e8)letatomic_obj9_encodinge0e1e2e3e4e5e6e7e8=atomic_obj2_encodinge0(atomic_obj8_encodinge1e2e3e4e5e6e7e8)letobj10_encodinge0e1e2e3e4e5e6e7e8e9=obj2_encodinge0(obj9_encodinge1e2e3e4e5e6e7e8e9)letatomic_obj10_encodinge0e1e2e3e4e5e6e7e8e9=atomic_obj2_encodinge0(atomic_obj9_encodinge1e2e3e4e5e6e7e8e9)endend