From bf530dedf7426141769c147bfebd44a9b71f56ca Mon Sep 17 00:00:00 2001 From: Rodrigo Ferreira Date: Thu, 16 Jan 2025 18:10:39 -0300 Subject: [PATCH 1/5] Adds the isolated test for honk calldata --- .../examples/proof_ultra_starknet.bin | Bin 0 -> 14340 bytes tests/hydra/test_honk_calldata.py | 6951 +++++++++++++++++ 2 files changed, 6951 insertions(+) create mode 100644 hydra/garaga/starknet/honk_contract_generator/examples/proof_ultra_starknet.bin create mode 100644 tests/hydra/test_honk_calldata.py diff --git a/hydra/garaga/starknet/honk_contract_generator/examples/proof_ultra_starknet.bin b/hydra/garaga/starknet/honk_contract_generator/examples/proof_ultra_starknet.bin new file mode 100644 index 0000000000000000000000000000000000000000..1f5a5b878e27bd4a5bab4b3a170d2df70113e9f9 GIT binary patch literal 14340 zcmeI(Ra8{_9>DPp3_1)wbPwGr4bmObC9N_@gLFwtgCZ$Xf(Q(U5Rq<>I5dJFh=jDH zG=lfM-tAiWa3AhkXU)9L{_Owy&wS@M^YkA8z-R}qe;}@B0ONmjOwjsR6=STF_YUo{ zFErF0*S-x5gB$=@>^6RpbAC194cNG($({kZgWlGTJbCO=P8Un0%%Xdk0>}Y?qIvzN z7fLEs;Jun1|VG^$8*b$S-V=mesK3Q~YOJl-Yub!KNSw z0DK9Z3#+#i(vGZhvv5mxkk3=(ohpPNTMd7r77P(TNk>5r05Bm-8wcd*_clFkzaU{b zkmKigyok@kgiSo)wYhS??owIPhU*7OqF z+C>wP0|4QVg;=Ngd`4EO+oFWUejxvU?_Z4_an~s5IAMX5bbCF3b|Vn<`Tzj$jp*ID z;FJ~*nJ276--1CtF{7I3T3T>`+sbHUyC~6n0&)OgYlF{i){sxB=*`~A7rhVi!DANs zMK?QrU(fLA&)zM_mmmiKP9KG&$*9Zw_%S7zN0J>Nzc5bXjo_4^{wCe-2K~a_djax) z=8poVR>j$2qP{oP%DNN)a{T0_La(k~NDhD;0LTb;qR8LT*C`8 z=X;`D>J9YZc!$ePVO(RXAnoY4^{g@j*@TTVPUr82Ccj&1 zJC2CfJffm#AEh@5DcqPQ#%NusQ(;`u)iQdzj!L~CYREwN_?`G^8 zu9hb*JB68B5d#}edKrNQvpXaQwE)TP(pT{N%sp%)Ku}dn&^~wB&40QwWGe}aIFL2R zCz@>Mu*OeU#8!P0E+)Z$i79(SG$V+)IQ_{~YhFtm%z|ESTX!H@gt=^nT$rUwQ->Ry zQS{kPljLZIS0FsWc~vHEv2p#X7NPBt6lEC(H+iqZ)K$*D;5&irI#d~&(Brj2dF2jl z6VjWMmON$k(HJ3Xa5XaYa1OapvS#iM+9Bf6xG}CMgY}%&zL2Ki$tG*oh|(ikaiL<< z6yaM*r3YUeS-M5kdnM3fT0Ypa=>qjMX%u(U^2 z5IXWe`kpGyoSfNkMPg8akQMaI>0n5bB(>bQmFGZIxz1f@hfPGQJN7!*A0>Bntdd3|SMtZ^_Kw>8Z^Jq#nUq|g6zrMLb)O4Zrf+O@D7xHV*{ z%pc!aM^{A0Fy#>mERGuF*CQdD^|!EcY#iZ3=dXf4NJLCB7S6+V;7SC;TY4C2o*s_iGGhO*3zhg-{tGXa0cLE0gry;2*YpqWO!iRG#FA zgOn7k!ku$(U(PYRDhyBHh8fEDG2@-MHR3USe80$r0;xTcjuUmh#ZYR3X`727$!Gqhm|8qUX1(8?F3LFz#r7?eobo!qO0@!dy|gC1RM#ZK8kR9iT1h60$lz zm#*7sdt>e`eX$5A$-N?Ed;goj9|{+jN~qylxuCJPv=NAXrG?O?6478?QP zCDFwZJCQ>>GGl=ElVSP@7j4*q;?S`JlGOk1oFG$MQwMzm)1i67%}<%!xT7^xN+v`U1KDx&XQWx&XQWx&XQWx&XQWx&XQWx&XQW zx&XQWx&XQWx&XQWx&XSszn}md{({qbmpPy+G@+?Bu)}9b)?*+sGVgW^hKe0OmWn>@ zOxMs*Z9ymmj{P!ELQaFdP8XUZ*}&y*@*)ZHg&GO9k}(f?C)sGEq{;I`vaRZ`@^9qD zIzi1dIU4$*KJ9-|$jn>1p55Ci)+esIY~DGY2^(uDwyA8DXr@{I8lFzYShLDv7@cio z4A%*Nx-Cguqei4-+#8?a_#%L_-o9=Y>W5*b&g7Nn(_)OB_Ik^rEX{D_SZJ}WUZdW? z_B?CLrt^B>_z^fda6VG(HmrKb7w;1B{=&8{ePvo51nUzzc zZ=vb1rp8!Hx!`Hau1kL$r)UI8^0cDS{^pM61L8J9O%Dv^NxJhYU*}7E{I6YAT)aWb zEB!qaL>Vegvv<_8o%)?CW4v%Ykko>vZ-rcKXjrVRCw(|#=#w}My;JhaOb5qWlE=T9 zC_hcKScYOA@lh77d*wWAOb!y|yZ?x@ke_@Dh28(imuZvPi4DaF=R9rhuZdPT?Oqjx z2D~a;zbHuIUvaIf2H2!dr|#BI;Xc`9Why&R#Z%&ucYh@iNir#oS4Tnu$INV>tjvF9bpDv8oD7$QednnT z=Pv*fys;ztXo!aTA(zp&-WEisXcxKrEoHmf4@X%tmRNk6i3fd!h?pms*=-j2>NG^X zyqtj!s}H_UHJqvbYQ{2<^<^sf`!U^bo&bs3RlpW>#V9y;pN= zR`3Ur`ki}cZr^L7eW{adPAPpI52a=A=q<*i)#Ta8xfR}C6DBn%ag6Bm*v}1yULoYo z%m=KQY7gH&MhY$%gh)qJbTzRuur{OUMod#*BJDw!ofc zDlX1-SC<$pO(Xc1+2m@It*j@knff5iBePVkSe-#;-k1-O9bRSh984=QSpqeVi#CK( zJ7>b97IGu2(-FjF^Q@L4uzA8-Jr?Tcyz#5DG$)}~yMY|fE+Zp?3p$qtsfcR~ngqBm z%9saQ;J3MtJQYWpwsD$Pr#TtUjSt(I94eS(1J#54{=&kl`O;%=cy3c;snle}?I}0H zw{}6qByU#m^Ub{5yDcn{Y&47c53~jNMQrKd>!UTZ zsR`|qPW<*KCot0DH4zU8A+HQ*sPuLx|L4Ss??IgBr)6G_ToIzFaC`)vWR$teI||Fe zI1Vys?ekK%aVM!ktf5wHyXR9gn%FFz3}3bk+La=G8dRSSYKJpC9SV_JC(3D`n|(+1 z))n(d%+63CmjZv{ey?`c+{%zWuZzu~uqm+=)=(U!HyK}#tmt`WtJCO6x3zzD6d&C| zgSgqm`-epx1cZNcQ;p+LVY!A>u0r-WxJFb{ArWc&6k&=iq>{Ei^5FL%FJ{acZ_^_*~8MLaqq4QKeHL(C|ZVW zzS}=~BXOvF5nKnH9S=SOH@NDxFuil>?#~-myES&>0blG=Y)C-!e^OUTW9W^~ZLR&1 zdr8QPs}$q_Ktdw2a&PmdPXc?$mbPm>$UUj(QKP;Y<4RRu{m(!5{8#}w0ATpC&Imc) z#4<^p_45=4pKnp(6zZi?;m-|&+@1+oP?;M<6$XJCZnRd zT86Q1l7Aj?(N!z?Z9T8}5e5P8#Le1p@qORKSdgDe*eo8q=%W-#T4ytNc?d{B4gl;? zo7@-Xn`g*LErpYQwqj_a?W_rJc+pA_ByPlc{` z^!#1l=TC~Bzdsea-qG`SeV;!mdj9@Y=z2%b-}Qa|r0DtkQ=#kqUq64CC9#zRM-o|G zQpL6$=DHV4s62_cH9PS=pYf%=R+?YYCK~nL-<4T<-SLd8<(A%Ew?JcvdB3Y9M|d0c zn~@j?LIs(1(|cJhfzDhRGQ7lB>Td#Rn#`5>7=q5$MCQ7RZq&U#zw8Yf9T4Zt$FGE{ zC`g+(jpe-G^Y70ML-r7AF_c8|tRB{%^L%1U$G>upB)Z_o|Mh{^F-bN1AnOBh+Vvj~ zTG0jmRRv;qzFyuJbX_ravh{{DHSPQV_U_o|Qfn0j0<-Yk;X7HA&q4lrO zPNFltnrvvj=Nq{j5v$Q9(FZ$1V5z3tRIAbBGQE?4UX1vvolpY**E zdwYs4d8`%}L=^{el%I={EYGukWD=Fa>vKk49gzR?|EnOJ1&&DK{iko#q4r{s{{W1@ B3dsNf literal 0 HcmV?d00001 diff --git a/tests/hydra/test_honk_calldata.py b/tests/hydra/test_honk_calldata.py new file mode 100644 index 00000000..81573f8c --- /dev/null +++ b/tests/hydra/test_honk_calldata.py @@ -0,0 +1,6951 @@ +import math +from abc import ABC, abstractmethod +from dataclasses import dataclass, fields +from pathlib import Path +from typing import List, Union + +import sha3 + +from garaga import garaga_rs +from garaga.algebra import BaseField, PyFelt +from garaga.definitions import ( + CURVES, + STARK, + CurveID, + G1G2Pair, + G1Point, + G2Point, + get_base_field, +) + +BATCHED_RELATION_PARTIAL_LENGTH = 8 +CONST_PROOF_SIZE_LOG_N = 28 +G1_PROOF_POINT_SHIFT = 2**136 +G2_POINT_KZG_1 = G2Point.get_nG(CurveID.BN254, 1) +G2_POINT_KZG_2 = G2Point( + x=( + 0x0118C4D5B837BCC2BC89B5B398B5974E9F5944073B32078B7E231FEC938883B0, + 0x260E01B251F6F1C7E7FF4E580791DEE8EA51D87A358E038B4EFE30FAC09383C1, + ), + y=( + 0x22FEBDA3C0C0632A56475B4214E5615E11E6DD3F96E6CEA2854A87D4DACC5E55, + 0x04FC6369F7110FE3D25156C1BB9A72859CF2A04641F99BA4EE413C80DA6A5FE4, + ), + curve_id=CurveID.BN254, +) +MAX_LOG_N = 23 # 2^23 = 8388608 +NUMBER_OF_SUBRELATIONS = 26 +NUMBER_OF_ALPHAS = NUMBER_OF_SUBRELATIONS - 1 +NUMBER_OF_ENTITIES = 44 +NUMBER_UNSHIFTED = 35 + + +def flatten(t): + result = [] + for item in t: + if isinstance(item, (tuple, list)): + result.extend(flatten(item)) + else: + result.append(item) + return result + + +def split_128(a: int) -> tuple[int, int]: + assert 0 <= a < 2**256, f"Value {a} is too large to fit in a u256" + return (a & ((1 << 128) - 1), a >> 128) + + +def bigint_split( + x: int | PyFelt | bytes, n_limbs: int = 4, base: int = 2**96 +) -> list[int]: + if isinstance(x, int): + pass + elif isinstance(x, PyFelt): + x = x.value + elif isinstance(x, bytes): + x = int.from_bytes(x, byteorder="big") + else: + raise ValueError(f"Invalid type for bigint_split: {type(x)}") + + coeffs = [] + degree = n_limbs - 1 + for n in range(degree, 0, -1): + q, r = divmod(x, base**n) + coeffs.append(q) + x = r + coeffs.append(x) + return coeffs[::-1] + + +def bigint_split_array( + x: list[int | PyFelt], + n_limbs: int = 4, + base: int = 2**96, + prepend_length=False, +) -> list[int]: + xs = [] + if prepend_length: + xs.append(len(x)) + for e in x: + xs.extend(bigint_split(e, n_limbs, base)) + return xs + + +@dataclass(slots=True) +class Cairo1SerializableStruct(ABC): + name: str + elmts: list[Union[PyFelt, "Cairo1SerializableStruct"]] + + def __post_init__(self): + assert type(self.name) == str + if isinstance(self.elmts, (list, tuple)): + if len(self.elmts) > 0: + if isinstance(self.elmts[0], Cairo1SerializableStruct): + assert all( + isinstance(elmt, self.elmts[0].__class__) for elmt in self.elmts + ), f"All elements of {self.name} must be of the same type" + + else: + assert all( + isinstance(elmt, PyFelt) for elmt in self.elmts + ), f"All elements of {self.name} must be of type PyFelt, got {type(self.elmts[0])}" + else: + assert self.elmts is None, f"Elmts must be a list or None, got {self.elmts}" + + @property + def struct_name(self) -> str: + return self.__class__.__name__ + + @abstractmethod + def __len__(self) -> int: + pass + + @abstractmethod + def _serialize_to_calldata(self) -> list[int]: + pass + + def serialize_to_calldata(self, *args, **kwargs) -> list[int]: + return self._serialize_to_calldata(*args, **kwargs) + + +class ModuloCircuit: + def __init__( + self, + curve_id: int, + ) -> None: + self.field = BaseField(CURVES[curve_id].p) + self.constants: dict[int, PyFelt] = dict() + self.input_structs: list[Cairo1SerializableStruct] = [] + + def write_element( + self, + elmt: PyFelt | int, + ) -> PyFelt: + if isinstance(elmt, int): + elmt = self.field(elmt) + return elmt + + def write_elements( + self, + elmts: list[PyFelt], + ) -> list[PyFelt]: + return [self.write_element(elmt) for elmt in elmts] + + def write_struct( + self, + struct: Cairo1SerializableStruct, + ) -> Union[ + PyFelt, + List[PyFelt], + List[List[Union[PyFelt, List[PyFelt]]]], + ]: + all_pyfelt = all(type(elmt) == PyFelt for elmt in struct.elmts) + all_cairo1serializablestruct = all( + isinstance(elmt, Cairo1SerializableStruct) for elmt in struct.elmts + ) + assert ( + all_pyfelt or all_cairo1serializablestruct + ), f"Expected list of PyFelt or Cairo1SerializableStruct, got {[type(elmt) for elmt in struct.elmts]}" + + if all_pyfelt: + self.input_structs.append(struct) + if len(struct) == 1 and isinstance(struct, u384): + return self.write_element(struct.elmts[0]) + else: + return self.write_elements(struct.elmts) + elif all_cairo1serializablestruct: + result = [self.write_struct(elmt, write_source) for elmt in struct.elmts] + # Ensure only the larger struct is appended + self.input_structs = [ + s for s in self.input_structs if s not in struct.elmts + ] + self.input_structs.append(struct) + return result + + def mul( + self, + a: PyFelt, + b: PyFelt, + ) -> PyFelt: + if a is None and isinstance(b, PyFelt): + return self.set_or_get_constant(0) + elif b is None and isinstance(a, PyFelt): + return self.set_or_get_constant(0) + assert isinstance(a, PyFelt) and isinstance( + b, PyFelt + ), f"Expected ModuloElement, got lhs {type(a)}, {a} and rhs {type(b)}, {b}" + return self.write_element(a * b) + + def add( + self, + a: PyFelt, + b: PyFelt, + ) -> PyFelt: + if a is None and isinstance(b, PyFelt): + return b + elif b is None and isinstance(a, PyFelt): + return a + else: + assert isinstance(a, PyFelt) and isinstance( + b, PyFelt + ), f"Expected ModuloElement, got {type(a)}, {a} and {type(b)}, {b}" + + return self.write_element(a + b) + + def sub( + self, + a: PyFelt, + b: PyFelt, + ): + return self.write_element(a.felt - b.felt) + + def double(self, a: PyFelt) -> PyFelt: + return self.add(a, a) + + def square(self, a: PyFelt) -> PyFelt: + return self.mul(a, a) + + def neg(self, a: PyFelt) -> PyFelt: + return self.sub(self.set_or_get_constant(self.field.zero()), a) + + def inv( + self, + a: PyFelt, + ): + return self.write_element(a.felt.__inv__()) + + def product(self, args: list[PyFelt]): + if not args: + raise ValueError("The 'args' list cannot be empty.") + assert all(isinstance(elmt, PyFelt) for elmt in args) + result = args[0] + for elmt in args[1:]: + result = self.mul(result, elmt) + return result + + def set_or_get_constant(self, val: PyFelt | int) -> PyFelt: + if isinstance(val, int): + val = self.field(val) + if val.value in self.constants: + return self.constants[val.value] + self.constants[val.value] = self.write_element(val) + return self.constants[val.value] + + +class G1PointCircuit(Cairo1SerializableStruct): + def __init__(self, name: str, elmts: list[PyFelt]): + super().__init__(name, elmts) + self.members_names = ("x", "y") + + @staticmethod + def from_G1Point(name: str, point: G1Point) -> "G1PointCircuit": + field = get_base_field(point.curve_id) + return G1PointCircuit(name=name, elmts=[field(point.x), field(point.y)]) + + @property + def struct_name(self) -> str: + return "G1Point" + + def _serialize_to_calldata(self) -> list[int]: + return bigint_split_array(self.elmts, prepend_length=False) + + def __len__(self) -> int: + if self.elmts is not None: + assert len(self.elmts) == 2 + return 2 + else: + return 2 + + +def hades_permutation(s0: int, s1: int, s2: int) -> tuple[int, int, int]: + r0, r1, r2 = garaga_rs.hades_permutation( + (s0 % STARK).to_bytes(32, "big"), + (s1 % STARK).to_bytes(32, "big"), + (s2 % STARK).to_bytes(32, "big"), + ) + return ( + int.from_bytes(r0, "big"), + int.from_bytes(r1, "big"), + int.from_bytes(r2, "big"), + ) + + +class Transcript(ABC): + def __init__(self): + self.reset() + + @abstractmethod + def reset(self): + pass + + @abstractmethod + def update(self, data: bytes): + pass + + @abstractmethod + def digest(self) -> bytes: + pass + + def digest_reset(self) -> bytes: + res_bytes = self.digest() + self.reset() + return res_bytes + + +class Sha3Transcript(Transcript): + def reset(self): + self.hasher = sha3.keccak_256() + + def digest(self) -> bytes: + res = self.hasher.digest() + res_int = int.from_bytes(res, "big") + res_mod = res_int % CURVES[CurveID.GRUMPKIN.value].p + res_bytes = res_mod.to_bytes(32, "big") + return res_bytes + + def update(self, data: bytes): + self.hasher.update(data) + + +class StarknetPoseidonTranscript(Transcript): + def reset(self): + self.s0, self.s1, self.s2 = hades_permutation( + int.from_bytes(b"StarknetHonk", "big"), 0, 1 + ) + + def digest(self) -> bytes: + res_bytes = self.s0.to_bytes(32, "big") + return res_bytes + + def update(self, data: bytes): + val = int.from_bytes(data, "big") + assert val < 2**256 + high, low = divmod(val, 2**128) + self.s0, self.s1, self.s2 = hades_permutation( + self.s0 + low, self.s1 + high, self.s2 + ) + + +@dataclass +class HonkVk: + name: str + circuit_size: int + log_circuit_size: int + public_inputs_size: int + public_inputs_offset: int + qm: G1Point + qc: G1Point + ql: G1Point + qr: G1Point + qo: G1Point + q4: G1Point + qArith: G1Point + qDeltaRange: G1Point + qElliptic: G1Point + qAux: G1Point + qLookup: G1Point + qPoseidon2External: G1Point + qPoseidon2Internal: G1Point + s1: G1Point + s2: G1Point + s3: G1Point + s4: G1Point + id1: G1Point + id2: G1Point + id3: G1Point + id4: G1Point + t1: G1Point + t2: G1Point + t3: G1Point + t4: G1Point + lagrange_first: G1Point + lagrange_last: G1Point + + @classmethod + def from_bytes(cls, bytes: bytes) -> "HonkVk": + circuit_size = int.from_bytes(bytes[0:8], "big") + log_circuit_size = int.from_bytes(bytes[8:16], "big") + public_inputs_size = int.from_bytes(bytes[16:24], "big") + public_inputs_offset = int.from_bytes(bytes[24:32], "big") + + cursor = 32 + + rest = bytes[cursor:] + assert len(rest) % 32 == 0 + + # Get all fields that are G1Points from the dataclass + g1_fields = [ + field.name + for field in fields(cls) + if field.type == G1Point and field.name != "name" + ] + + # Parse all G1Points into a dictionary + points = {} + for field_name in g1_fields: + x = int.from_bytes(bytes[cursor : cursor + 32], "big") + y = int.from_bytes(bytes[cursor + 32 : cursor + 64], "big") + points[field_name] = G1Point(x=x, y=y, curve_id=CurveID.BN254) + cursor += 64 + + # Create instance with all parsed values + return cls( + name="", + circuit_size=circuit_size, + log_circuit_size=log_circuit_size, + public_inputs_size=public_inputs_size, + public_inputs_offset=public_inputs_offset, + **points, + ) + + def to_circuit_elements(self, circuit: ModuloCircuit) -> "HonkVk": + return HonkVk( + name=self.name, + circuit_size=self.circuit_size, + log_circuit_size=self.log_circuit_size, + public_inputs_size=self.public_inputs_size, + public_inputs_offset=circuit.write_element(self.public_inputs_offset), + **{ + field.name: circuit.write_struct( + G1PointCircuit.from_G1Point(field.name, getattr(self, field.name)) + ) + for field in fields(self) + if field.type == G1Point and field.name != "name" + }, + ) + + +@dataclass +class HonkProof: + circuit_size: int + public_inputs_size: int + public_inputs_offset: int + public_inputs: list[int] + w1: G1Point + w2: G1Point + w3: G1Point + w4: G1Point + z_perm: G1Point + lookup_read_counts: G1Point + lookup_read_tags: G1Point + lookup_inverses: G1Point + sumcheck_univariates: list[list[int]] + sumcheck_evaluations: list[int] + gemini_fold_comms: list[G1Point] + gemini_a_evaluations: list[int] + shplonk_q: G1Point + kzg_quotient: G1Point + + @property + def log_circuit_size(self) -> int: + return int(math.log2(self.circuit_size)) + + def __post_init__(self): + assert len(self.sumcheck_univariates) == CONST_PROOF_SIZE_LOG_N + assert all( + len(univariate) == BATCHED_RELATION_PARTIAL_LENGTH + for univariate in self.sumcheck_univariates + ) + assert len(self.sumcheck_evaluations) == NUMBER_OF_ENTITIES + assert len(self.gemini_fold_comms) == CONST_PROOF_SIZE_LOG_N - 1 + assert len(self.gemini_a_evaluations) == CONST_PROOF_SIZE_LOG_N + + @classmethod + def from_bytes(cls, bytes: bytes) -> "HonkProof": + n_elements = int.from_bytes(bytes[:4], "big") + assert len(bytes[4:]) % 32 == 0 + elements = [ + int.from_bytes(bytes[i : i + 32], "big") for i in range(4, len(bytes), 32) + ] + assert len(elements) == n_elements + + circuit_size = elements[0] + public_inputs_size = elements[1] + public_inputs_offset = elements[2] + + assert circuit_size <= 2**MAX_LOG_N + + public_inputs = [] + cursor = 3 + for i in range(public_inputs_size): + public_inputs.append(elements[cursor + i]) + + cursor += public_inputs_size + + def parse_g1_proof_point(i: int) -> G1Point: + return G1Point( + x=elements[i] + G1_PROOF_POINT_SHIFT * elements[i + 1], + y=elements[i + 2] + G1_PROOF_POINT_SHIFT * elements[i + 3], + curve_id=CurveID.BN254, + ) + + G1_PROOF_POINT_SIZE = 4 + + w1 = parse_g1_proof_point(cursor) + w2 = parse_g1_proof_point(cursor + G1_PROOF_POINT_SIZE) + w3 = parse_g1_proof_point(cursor + 2 * G1_PROOF_POINT_SIZE) + + lookup_read_counts = parse_g1_proof_point(cursor + 3 * G1_PROOF_POINT_SIZE) + lookup_read_tags = parse_g1_proof_point(cursor + 4 * G1_PROOF_POINT_SIZE) + w4 = parse_g1_proof_point(cursor + 5 * G1_PROOF_POINT_SIZE) + lookup_inverses = parse_g1_proof_point(cursor + 6 * G1_PROOF_POINT_SIZE) + z_perm = parse_g1_proof_point(cursor + 7 * G1_PROOF_POINT_SIZE) + + cursor += 8 * G1_PROOF_POINT_SIZE + + # Parse sumcheck univariates. + sumcheck_univariates = [] + for i in range(CONST_PROOF_SIZE_LOG_N): + sumcheck_univariates.append( + [ + elements[cursor + i * BATCHED_RELATION_PARTIAL_LENGTH + j] + for j in range(BATCHED_RELATION_PARTIAL_LENGTH) + ] + ) + cursor += BATCHED_RELATION_PARTIAL_LENGTH * CONST_PROOF_SIZE_LOG_N + + # Parse sumcheck_evaluations + sumcheck_evaluations = elements[cursor : cursor + NUMBER_OF_ENTITIES] + + cursor += NUMBER_OF_ENTITIES + + # Parse gemini fold comms + gemini_fold_comms = [ + parse_g1_proof_point(cursor + i * G1_PROOF_POINT_SIZE) + for i in range(CONST_PROOF_SIZE_LOG_N - 1) + ] + + cursor += (CONST_PROOF_SIZE_LOG_N - 1) * G1_PROOF_POINT_SIZE + + # Parse gemini a evaluations + gemini_a_evaluations = elements[cursor : cursor + CONST_PROOF_SIZE_LOG_N] + + cursor += CONST_PROOF_SIZE_LOG_N + + shplonk_q = parse_g1_proof_point(cursor) + kzg_quotient = parse_g1_proof_point(cursor + G1_PROOF_POINT_SIZE) + + cursor += 2 * G1_PROOF_POINT_SIZE + + assert cursor == len(elements) + + return HonkProof( + circuit_size=circuit_size, + public_inputs_size=public_inputs_size, + public_inputs_offset=public_inputs_offset, + public_inputs=public_inputs, + w1=w1, + w2=w2, + w3=w3, + w4=w4, + z_perm=z_perm, + lookup_read_counts=lookup_read_counts, + lookup_read_tags=lookup_read_tags, + lookup_inverses=lookup_inverses, + sumcheck_univariates=sumcheck_univariates, + sumcheck_evaluations=sumcheck_evaluations, + gemini_fold_comms=gemini_fold_comms, + gemini_a_evaluations=gemini_a_evaluations, + shplonk_q=shplonk_q, + kzg_quotient=kzg_quotient, + ) + + def to_circuit_elements(self, circuit: ModuloCircuit) -> "HonkProof": + """Convert everything to PyFelts given a circuit.""" + return HonkProof( + circuit_size=self.circuit_size, + public_inputs_size=self.public_inputs_size, + public_inputs_offset=circuit.write_element(self.public_inputs_offset), + public_inputs=circuit.write_elements(self.public_inputs), + w1=circuit.write_struct(G1PointCircuit.from_G1Point("w1", self.w1)), + w2=circuit.write_struct(G1PointCircuit.from_G1Point("w2", self.w2)), + w3=circuit.write_struct(G1PointCircuit.from_G1Point("w3", self.w3)), + w4=circuit.write_struct(G1PointCircuit.from_G1Point("w4", self.w4)), + z_perm=circuit.write_struct( + G1PointCircuit.from_G1Point("z_perm", self.z_perm) + ), + lookup_read_counts=circuit.write_struct( + G1PointCircuit.from_G1Point( + "lookup_read_counts", self.lookup_read_counts + ) + ), + lookup_read_tags=circuit.write_struct( + G1PointCircuit.from_G1Point("lookup_read_tags", self.lookup_read_tags) + ), + lookup_inverses=circuit.write_struct( + G1PointCircuit.from_G1Point("lookup_inverses", self.lookup_inverses) + ), + sumcheck_univariates=[ + circuit.write_elements(univariate) + for univariate in self.sumcheck_univariates + ], + sumcheck_evaluations=circuit.write_elements(self.sumcheck_evaluations), + gemini_fold_comms=[ + circuit.write_struct( + G1PointCircuit.from_G1Point(f"gemini_fold_comm_{i}", comm) + ) + for i, comm in enumerate(self.gemini_fold_comms) + ], + gemini_a_evaluations=circuit.write_elements(self.gemini_a_evaluations), + shplonk_q=circuit.write_struct( + G1PointCircuit.from_G1Point("shplonk_q", self.shplonk_q) + ), + kzg_quotient=circuit.write_struct( + G1PointCircuit.from_G1Point("kzg_quotient", self.kzg_quotient) + ), + ) + + def serialize_to_calldata(self) -> list[int]: + def serialize_G1Point256(g1_point: G1Point) -> list[int]: + xl, xh = split_128(g1_point.x) + yl, yh = split_128(g1_point.y) + return [xl, xh, yl, yh] + + cd = [] + cd.append(self.circuit_size) + cd.append(self.public_inputs_size) + cd.append(self.public_inputs_offset) + cd.extend( + bigint_split_array( + x=self.public_inputs, n_limbs=2, base=2**128, prepend_length=True + ) + ) + cd.extend(serialize_G1Point256(self.w1)) + cd.extend(serialize_G1Point256(self.w2)) + cd.extend(serialize_G1Point256(self.w3)) + cd.extend(serialize_G1Point256(self.w4)) + cd.extend(serialize_G1Point256(self.z_perm)) + cd.extend(serialize_G1Point256(self.lookup_read_counts)) + cd.extend(serialize_G1Point256(self.lookup_read_tags)) + cd.extend(serialize_G1Point256(self.lookup_inverses)) + cd.extend( + bigint_split_array( + x=flatten(self.sumcheck_univariates)[ + : BATCHED_RELATION_PARTIAL_LENGTH * self.log_circuit_size + ], # The rest is 0. + n_limbs=2, + base=2**128, + prepend_length=True, + ) + ) + + cd.extend( + bigint_split_array( + x=self.sumcheck_evaluations, n_limbs=2, base=2**128, prepend_length=True + ) + ) + + cd.append(self.log_circuit_size - 1) + for pt in self.gemini_fold_comms[ + : self.log_circuit_size - 1 + ]: # The rest is G(1, 2) + cd.extend(serialize_G1Point256(pt)) + + cd.extend( + bigint_split_array( + x=self.gemini_a_evaluations[: self.log_circuit_size], + n_limbs=2, + base=2**128, + prepend_length=True, + ) + ) + cd.extend(serialize_G1Point256(self.shplonk_q)) + cd.extend(serialize_G1Point256(self.kzg_quotient)) + + return cd + + +@dataclass +class HonkTranscript: + eta: int | PyFelt + etaTwo: int | PyFelt + etaThree: int | PyFelt + beta: int | PyFelt + gamma: int | PyFelt + alphas: list[int | PyFelt] + gate_challenges: list[int | PyFelt] + sum_check_u_challenges: list[PyFelt] + rho: int | PyFelt + gemini_r: int | PyFelt + shplonk_nu: int | PyFelt + shplonk_z: int | PyFelt + public_inputs_delta: int | None = None # Derived. + + def __post_init__(self): + assert len(self.alphas) == NUMBER_OF_ALPHAS + assert len(self.gate_challenges) == CONST_PROOF_SIZE_LOG_N + assert len(self.sum_check_u_challenges) == CONST_PROOF_SIZE_LOG_N + + @classmethod + def from_proof(cls, proof: HonkProof, system="UltraKeccakHonk") -> "HonkTranscript": + def g1_to_g1_proof_point(g1_proof_point: G1Point) -> tuple[int, int, int, int]: + x_high, x_low = divmod(g1_proof_point.x, G1_PROOF_POINT_SHIFT) + y_high, y_low = divmod(g1_proof_point.y, G1_PROOF_POINT_SHIFT) + return (x_low, x_high, y_low, y_high) + + def split_challenge(ch: bytes) -> tuple[int, int]: + ch_int = int.from_bytes(ch, "big") + high_128, low_128 = divmod(ch_int, 2**128) + return (low_128, high_128) + + # Round 0 : circuit_size, public_inputs_size, public_input_offset, [public_inputs], w1, w2, w3 + FR = CURVES[CurveID.GRUMPKIN.value].p + + match system: + case "UltraKeccakHonk": + hasher = Sha3Transcript() + case "UltraStarknetHonk": + hasher = StarknetPoseidonTranscript() + case _: + raise ValueError(f"Proof system {system} not compatible") + + hasher.update(int.to_bytes(proof.circuit_size, 32, "big")) + hasher.update(int.to_bytes(proof.public_inputs_size, 32, "big")) + hasher.update(int.to_bytes(proof.public_inputs_offset, 32, "big")) + + for pub_input in proof.public_inputs: + hasher.update(int.to_bytes(pub_input, 32, "big")) + + for g1_proof_point in [proof.w1, proof.w2, proof.w3]: + # print(f"g1_proof_point: {g1_proof_point.__repr__()}") + x0, x1, y0, y1 = g1_to_g1_proof_point(g1_proof_point) + hasher.update(int.to_bytes(x0, 32, "big")) + hasher.update(int.to_bytes(x1, 32, "big")) + hasher.update(int.to_bytes(y0, 32, "big")) + hasher.update(int.to_bytes(y1, 32, "big")) + + ch0 = hasher.digest_reset() + + eta, eta_two = split_challenge(ch0) + + hasher.update(ch0) + ch0 = hasher.digest_reset() + eta_three, _ = split_challenge(ch0) + + # Round 1 : ch0, lookup_read_counts, lookup_read_tags, w4 + + hasher.update(ch0) + + for g1_proof_point in [ + proof.lookup_read_counts, + proof.lookup_read_tags, + proof.w4, + ]: + x0, x1, y0, y1 = g1_to_g1_proof_point(g1_proof_point) + hasher.update(int.to_bytes(x0, 32, "big")) + hasher.update(int.to_bytes(x1, 32, "big")) + hasher.update(int.to_bytes(y0, 32, "big")) + hasher.update(int.to_bytes(y1, 32, "big")) + + ch1 = hasher.digest_reset() + beta, gamma = split_challenge(ch1) + + # Round 2: ch1, lookup_inverses, z_perm + + hasher.update(ch1) + + for g1_proof_point in [proof.lookup_inverses, proof.z_perm]: + x0, x1, y0, y1 = g1_to_g1_proof_point(g1_proof_point) + hasher.update(int.to_bytes(x0, 32, "big")) + hasher.update(int.to_bytes(x1, 32, "big")) + hasher.update(int.to_bytes(y0, 32, "big")) + hasher.update(int.to_bytes(y1, 32, "big")) + + ch2 = hasher.digest_reset() + + alphas = [None] * NUMBER_OF_ALPHAS + alphas[0], alphas[1] = split_challenge(ch2) + + for i in range(1, NUMBER_OF_ALPHAS // 2): + hasher.update(ch2) + ch2 = hasher.digest_reset() + alphas[i * 2], alphas[i * 2 + 1] = split_challenge(ch2) + + if NUMBER_OF_ALPHAS % 2 == 1: + hasher.update(ch2) + ch2 = hasher.digest_reset() + alphas[-1], _ = split_challenge(ch2) + + # Round 3: Gate Challenges : + ch3 = ch2 + gate_challenges = [None] * CONST_PROOF_SIZE_LOG_N + for i in range(CONST_PROOF_SIZE_LOG_N): + hasher.update(ch3) + ch3 = hasher.digest_reset() + gate_challenges[i], _ = split_challenge(ch3) + + # Round 4: Sumcheck u challenges + ch4 = ch3 + sum_check_u_challenges = [None] * CONST_PROOF_SIZE_LOG_N + + for i in range(CONST_PROOF_SIZE_LOG_N): + # Create array of univariate challenges starting with previous challenge + univariate_chal = [ch4] + + # Add the sumcheck univariates for this round + for j in range(BATCHED_RELATION_PARTIAL_LENGTH): + univariate_chal.append( + int.to_bytes(proof.sumcheck_univariates[i][j], 32, "big") + ) + + # Update hasher with all univariate challenges + for chal in univariate_chal: + hasher.update(chal) + + # Get next challenge + ch4 = hasher.digest_reset() + + # Split challenge to get sumcheck challenge + sum_check_u_challenges[i], _ = split_challenge(ch4) + + # Rho challenge : + hasher.update(ch4) + for i in range(NUMBER_OF_ENTITIES): + hasher.update(int.to_bytes(proof.sumcheck_evaluations[i], 32, "big")) + + c5 = hasher.digest_reset() + rho, _ = split_challenge(c5) + + # Gemini R : + hasher.update(c5) + for i in range(CONST_PROOF_SIZE_LOG_N - 1): + x0, x1, y0, y1 = g1_to_g1_proof_point(proof.gemini_fold_comms[i]) + hasher.update(int.to_bytes(x0, 32, "big")) + hasher.update(int.to_bytes(x1, 32, "big")) + hasher.update(int.to_bytes(y0, 32, "big")) + hasher.update(int.to_bytes(y1, 32, "big")) + + c6 = hasher.digest_reset() + gemini_r, _ = split_challenge(c6) + + # Shplonk Nu : + hasher.update(c6) + for i in range(CONST_PROOF_SIZE_LOG_N): + hasher.update(int.to_bytes(proof.gemini_a_evaluations[i], 32, "big")) + + c7 = hasher.digest_reset() + shplonk_nu, _ = split_challenge(c7) + + # Shplonk Z : + hasher.update(c7) + x0, x1, y0, y1 = g1_to_g1_proof_point(proof.shplonk_q) + hasher.update(int.to_bytes(x0, 32, "big")) + hasher.update(int.to_bytes(x1, 32, "big")) + hasher.update(int.to_bytes(y0, 32, "big")) + hasher.update(int.to_bytes(y1, 32, "big")) + + c8 = hasher.digest_reset() + shplonk_z, _ = split_challenge(c8) + + return cls( + eta=eta, + etaTwo=eta_two, + etaThree=eta_three, + beta=beta, + gamma=gamma, + alphas=alphas, + gate_challenges=gate_challenges, + sum_check_u_challenges=sum_check_u_challenges, + rho=rho, + gemini_r=gemini_r, + shplonk_nu=shplonk_nu, + shplonk_z=shplonk_z, + public_inputs_delta=None, + ) + + def to_circuit_elements(self, circuit: ModuloCircuit) -> "HonkTranscript": + return HonkTranscript( + eta=circuit.write_element(self.eta), + etaTwo=circuit.write_element(self.etaTwo), + etaThree=circuit.write_element(self.etaThree), + beta=circuit.write_element(self.beta), + gamma=circuit.write_element(self.gamma), + alphas=circuit.write_elements(self.alphas), + gate_challenges=circuit.write_elements(self.gate_challenges), + sum_check_u_challenges=circuit.write_elements(self.sum_check_u_challenges), + rho=circuit.write_element(self.rho), + gemini_r=circuit.write_element(self.gemini_r), + shplonk_nu=circuit.write_element(self.shplonk_nu), + shplonk_z=circuit.write_element(self.shplonk_z), + public_inputs_delta=None, + ) + + +class HonkVerifierCircuits(ModuloCircuit): + def __init__( + self, + log_n: int, + curve_id: int = CurveID.GRUMPKIN.value, + ): + super().__init__( + curve_id=curve_id, + ) + self.log_n = log_n + + def compute_shplemini_msm_scalars( + self, + p_sumcheck_evaluations: list[PyFelt], # Full evaluations, not replaced. + p_gemini_a_evaluations: list[PyFelt], + tp_gemini_r: PyFelt, + tp_rho: PyFelt, + tp_shplonk_z: PyFelt, + tp_shplonk_nu: PyFelt, + tp_sumcheck_u_challenges: list[PyFelt], + ) -> list[PyFelt]: + assert all(isinstance(i, PyFelt) for i in p_sumcheck_evaluations) + # function computeSquares(Fr r) internal pure returns (Fr[CONST_PROOF_SIZE_LOG_N] memory squares) { + # squares[0] = r; + # for (uint256 i = 1; i < CONST_PROOF_SIZE_LOG_N; ++i) { + # squares[i] = squares[i - 1].sqr(); + # } + # } + powers_of_evaluations_challenge = [tp_gemini_r] + for i in range(1, self.log_n): + powers_of_evaluations_challenge.append( + self.mul( + powers_of_evaluations_challenge[i - 1], + powers_of_evaluations_challenge[i - 1], + ) + ) + + scalars = [self.set_or_get_constant(0)] * ( + NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2 + ) + + # computeInvertedGeminiDenominators + + inverse_vanishing_evals = [None] * (CONST_PROOF_SIZE_LOG_N + 1) + inverse_vanishing_evals[0] = self.inv( + self.sub(tp_shplonk_z, powers_of_evaluations_challenge[0]) + ) + for i in range(self.log_n): + inverse_vanishing_evals[i + 1] = self.inv( + self.add(tp_shplonk_z, powers_of_evaluations_challenge[i]) + ) + assert len(inverse_vanishing_evals) == CONST_PROOF_SIZE_LOG_N + 1 + + # mem.unshiftedScalar = inverse_vanishing_evals[0] + (tp.shplonkNu * inverse_vanishing_evals[1]); + # mem.shiftedScalar = + # tp.geminiR.invert() * (inverse_vanishing_evals[0] - (tp.shplonkNu * inverse_vanishing_evals[1])); + + unshifted_scalar = self.neg( + self.add( + inverse_vanishing_evals[0], + self.mul(tp_shplonk_nu, inverse_vanishing_evals[1]), + ) + ) + + shifted_scalar = self.neg( + self.mul( + self.inv(tp_gemini_r), + self.sub( + inverse_vanishing_evals[0], + self.mul(tp_shplonk_nu, inverse_vanishing_evals[1]), + ), + ) + ) + + scalars[0] = self.set_or_get_constant(1) + + batching_challenge = self.set_or_get_constant(1) + batched_evaluation = self.set_or_get_constant(0) + + for i in range(1, NUMBER_UNSHIFTED + 1): + scalars[i] = self.mul(unshifted_scalar, batching_challenge) + batched_evaluation = self.add( + batched_evaluation, + self.mul(p_sumcheck_evaluations[i - 1], batching_challenge), + ) + batching_challenge = self.mul(batching_challenge, tp_rho) + + for i in range(NUMBER_UNSHIFTED + 1, NUMBER_OF_ENTITIES + 1): + scalars[i] = self.mul(shifted_scalar, batching_challenge) + batched_evaluation = self.add( + batched_evaluation, + self.mul(p_sumcheck_evaluations[i - 1], batching_challenge), + ) + # skip last round: + if i < NUMBER_OF_ENTITIES: + batching_challenge = self.mul(batching_challenge, tp_rho) + + constant_term_accumulator = self.set_or_get_constant(0) + batching_challenge = self.square(tp_shplonk_nu) + + for i in range(CONST_PROOF_SIZE_LOG_N - 1): + dummy_round = i >= (self.log_n - 1) + + scaling_factor = self.set_or_get_constant(0) + if not dummy_round: + scaling_factor = self.mul( + batching_challenge, inverse_vanishing_evals[i + 2] + ) + scalars[NUMBER_OF_ENTITIES + i + 1] = self.neg(scaling_factor) + constant_term_accumulator = self.add( + constant_term_accumulator, + self.mul(scaling_factor, p_gemini_a_evaluations[i + 1]), + ) + else: + # print( + # f"dummy round {i}, index {NUMBER_OF_ENTITIES + i + 1} is set to 0" + # ) + pass + + # skip last round: + if i < self.log_n - 2: + batching_challenge = self.mul(batching_challenge, tp_shplonk_nu) + + # computeGeminiBatchedUnivariateEvaluation + def compute_gemini_batched_univariate_evaluation( + tp_sumcheck_u_challenges, + batched_eval_accumulator, + gemini_evaluations, + gemini_eval_challenge_powers, + ): + for i in range(self.log_n, 0, -1): + challenge_power = gemini_eval_challenge_powers[i - 1] + u = tp_sumcheck_u_challenges[i - 1] + eval_neg = gemini_evaluations[i - 1] + + # (challengePower * batchedEvalAccumulator * Fr.wrap(2)) - evalNeg * (challengePower * (Fr.wrap(1) - u) - u)) + # (challengePower * (Fr.wrap(1) - u) + term = self.mul( + challenge_power, self.sub(self.set_or_get_constant(1), u) + ) + + batched_eval_round_acc = self.sub( + self.double(self.mul(challenge_power, batched_eval_accumulator)), + self.mul(eval_neg, self.sub(term, u)), + ) + + # (challengePower * (Fr.wrap(1) - u) + u).invert() + den = self.add(term, u) + + batched_eval_round_acc = self.mul(batched_eval_round_acc, self.inv(den)) + batched_eval_accumulator = batched_eval_round_acc + + return batched_eval_accumulator + + a_0_pos = compute_gemini_batched_univariate_evaluation( + tp_sumcheck_u_challenges, + batched_evaluation, + p_gemini_a_evaluations, + powers_of_evaluations_challenge, + ) + + # mem.constantTermAccumulator = mem.constantTermAccumulator + (a_0_pos * inverse_vanishing_evals[0]); + # mem.constantTermAccumulator = + # mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * inverse_vanishing_evals[1]); + + constant_term_accumulator = self.add( + constant_term_accumulator, + self.mul(a_0_pos, inverse_vanishing_evals[0]), + ) + + constant_term_accumulator = self.add( + constant_term_accumulator, + self.product( + [ + p_gemini_a_evaluations[0], + tp_shplonk_nu, + inverse_vanishing_evals[1], + ] + ), + ) + + scalars[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N] = constant_term_accumulator + scalars[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 1] = tp_shplonk_z + + # vk.t1 : 22 + 36 + # vk.t2 : 23 + 37 + # vk.t3 : 24 + 38 + # vk.t4 : 25 + 39 + + # proof.w1 : 28 + 40 + # proof.w2 : 29 + 41 + # proof.w3 : 30 + 42 + # proof.w4 : 31 + 43 + + scalars[22] = self.add(scalars[22], scalars[36]) + scalars[23] = self.add(scalars[23], scalars[37]) + scalars[24] = self.add(scalars[24], scalars[38]) + scalars[25] = self.add(scalars[25], scalars[39]) + + scalars[28] = self.add(scalars[28], scalars[40]) + scalars[29] = self.add(scalars[29], scalars[41]) + scalars[30] = self.add(scalars[30], scalars[42]) + scalars[31] = self.add(scalars[31], scalars[43]) + + scalars[36] = None + scalars[37] = None + scalars[38] = None + scalars[39] = None + scalars[40] = None + scalars[41] = None + scalars[42] = None + scalars[43] = None + + return scalars + + +@dataclass(slots=True) +class MPCheckCalldataBuilder: + curve_id: CurveID + pairs: list[G1G2Pair] + n_fixed_g2: int + public_pair: G1G2Pair | None + + def __post_init__(self): + # Validate input + assert isinstance(self.pairs, (list, tuple)) + assert all( + isinstance(pair, G1G2Pair) for pair in self.pairs + ), f"All pairs must be G1G2Pair, got {[type(pair) for pair in self.pairs]}" + assert all( + self.curve_id == pair.curve_id == self.pairs[0].curve_id + for pair in self.pairs + ), f"All pairs must be on the same curve, got {[pair.curve_id for pair in self.pairs]}" + assert ( + isinstance(self.public_pair, G1G2Pair) or self.public_pair is None + ), f"Extra pair must be G1G2Pair or None, got {self.public_pair}" + assert len(self.pairs) >= 2 + assert 0 <= self.n_fixed_g2 <= len(self.pairs) + + def serialize_to_calldata(self) -> list[int]: + return garaga_rs.mpc_calldata_builder( + self.curve_id.value, + [element.value for pair in self.pairs for element in pair.to_pyfelt_list()], + self.n_fixed_g2, + ( + [element.value for element in self.public_pair.to_pyfelt_list()] + if self.public_pair is not None + else [] + ), + ) + + +@dataclass(slots=True) +class MSMCalldataBuilder: + curve_id: CurveID + points: list[G1Point] + scalars: list[int] + + def __post_init__(self): + assert all( + point.curve_id == self.curve_id for point in self.points + ), "All points must be on the same curve." + assert len(self.points) == len( + self.scalars + ), "Number of points and scalars must be equal." + assert all( + 0 <= s <= CURVES[self.curve_id.value].n for s in self.scalars + ), f"Scalars must be in [0, {self.curve_id.name}'s order] == [0, {CURVES[self.curve_id.value].n}]." + + def serialize_to_calldata( + self, + include_digits_decomposition=True, + include_points_and_scalars=True, + serialize_as_pure_felt252_array=False, + ) -> list[int]: + return garaga_rs.msm_calldata_builder( + [value for point in self.points for value in [point.x, point.y]], + self.scalars, + self.curve_id.value, + include_digits_decomposition, + include_points_and_scalars, + serialize_as_pure_felt252_array, + False, + ) + + +def extract_msm_scalars(scalars: list[PyFelt], log_n: int) -> list[int]: + assert len(scalars) == NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2 + + start_dummy = NUMBER_OF_ENTITIES + log_n + end_dummy = NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + + scalars_no_dummy = scalars[:start_dummy] + scalars[end_dummy:] + + scalars_filtered = scalars_no_dummy[1:] + scalars_filtered_no_nones = [ + scalar for scalar in scalars_filtered if scalar is not None + ] + return [s.value for s in scalars_filtered_no_nones] + + +def get_ultra_flavor_honk_calldata_from_vk_and_proof( + system: str, vk: HonkVk, proof: HonkProof +) -> list[int]: + tp = HonkTranscript.from_proof(proof, system) + + circuit = HonkVerifierCircuits(log_n=vk.log_circuit_size) + + vk_circuit = vk.to_circuit_elements(circuit) + proof_circuit = proof.to_circuit_elements(circuit) + tp = tp.to_circuit_elements(circuit) + + scalars = circuit.compute_shplemini_msm_scalars( + proof_circuit.sumcheck_evaluations, + proof_circuit.gemini_a_evaluations, + tp.gemini_r, + tp.rho, + tp.shplonk_z, + tp.shplonk_nu, + tp.sum_check_u_challenges, + ) + + scalars_msm = extract_msm_scalars(scalars, vk.log_circuit_size) + + points = [ + vk.qm, # 1 + vk.qc, # 2 + vk.ql, # 3 + vk.qr, # 4 + vk.qo, # 5 + vk.q4, # 6 + vk.qArith, # 7 + vk.qDeltaRange, # 8 + vk.qElliptic, # 9 + vk.qAux, # 10 + vk.qLookup, # 11 + vk.qPoseidon2External, # 12 + vk.qPoseidon2Internal, # 13 + vk.s1, # 14 + vk.s2, # 15 + vk.s3, # 16 + vk.s4, # 17 + vk.id1, # 18 + vk.id2, # 19 + vk.id3, # 20 + vk.id4, # 21 + vk.t1, # 22 + vk.t2, # 23 + vk.t3, # 24 + vk.t4, # 25 + vk.lagrange_first, # 26 + vk.lagrange_last, # 27 + proof.w1, # 28 + proof.w2, # 29 + proof.w3, # 30 + proof.w4, # 31 + proof.z_perm, # 32 + proof.lookup_inverses, # 33 + proof.lookup_read_counts, # 34 + proof.lookup_read_tags, # 35 + proof.z_perm, # 44 + ] + points.extend(proof.gemini_fold_comms[: vk.log_circuit_size - 1]) + points.append(G1Point.get_nG(CurveID.BN254, 1)) + points.append(proof.kzg_quotient) + + msm_builder = MSMCalldataBuilder(CurveID.BN254, points=points, scalars=scalars_msm) + + P_0 = G1Point.msm(points=points, scalars=scalars_msm).add(proof.shplonk_q) + P_1 = -proof.kzg_quotient + + pairs = [G1G2Pair(P_0, G2_POINT_KZG_1), G1G2Pair(P_1, G2_POINT_KZG_2)] + + mpc_builder = MPCheckCalldataBuilder( + curve_id=CurveID.BN254, pairs=pairs, n_fixed_g2=2, public_pair=None + ) + cd = [] + cd.extend(proof.serialize_to_calldata()) + cd.extend( + msm_builder.serialize_to_calldata( + include_points_and_scalars=False, + serialize_as_pure_felt252_array=False, + include_digits_decomposition=False, + ) + ) + cd.extend(mpc_builder.serialize_to_calldata()) + + res = [len(cd)] + cd + + # print(f"HONK CALLDATA: {res}") + # print(f"HONK CALLDATA LENGTH: {len(res)}") + + return res + + +def get_honk_calldata(system: str, vk: Path, proof: Path) -> list[int]: + vk_obj = HonkVk.from_bytes(open(vk, "rb").read()) + proof_obj = HonkProof.from_bytes(open(proof, "rb").read()) + return get_ultra_flavor_honk_calldata_from_vk_and_proof(system, vk_obj, proof_obj) + + +def main(): + script_path = Path(__file__).resolve() + script_folder_path = script_path.parent + examples_folder_path = ( + script_folder_path.parent.parent + / "hydra" + / "garaga" + / "starknet" + / "honk_contract_generator" + / "examples" + ) + vk = examples_folder_path / "vk_ultra_keccak.bin" + proof = examples_folder_path / "proof_ultra_keccak.bin" + calldata = get_honk_calldata("UltraKeccakHonk", vk, proof) + # fix for garaga_rs.msm + calldata = calldata[:245] + calldata[246:] + calldata[0] -= 1 + assert calldata == ULTRA_KECCAK_CALLDATA + + vk = examples_folder_path / "vk_ultra_keccak.bin" + proof = examples_folder_path / "proof_ultra_starknet.bin" + calldata = get_honk_calldata("UltraStarknetHonk", vk, proof) + # fix for garaga_rs.msm + calldata = calldata[:245] + calldata[246:] + calldata[0] -= 1 + assert calldata == ULTRA_STARKNET_CALLDATA + + print("success") + + +ULTRA_KECCAK_CALLDATA = [ + 2808, + 32, + 1, + 1, + 1, + 2, + 0, + 93492569705772761183096382943568120526, + 48381375842447371862176894966487046537, + 108892489790625465941038134709497921858, + 31553121233089882260783539337434819177, + 313788157447883438835058217963236691121, + 19570789212101600643721604454518588879, + 63121438458343251469824282752325944217, + 46640180724269545065992330023439179969, + 128137402165770828503488690514142526808, + 17577547092075648337766919109542225915, + 31402854409629339723711987293584364752, + 13553788703392864169478279281666965726, + 7328163057324986380960685547530807675, + 16818951948308270205702238282801511857, + 288813996749633455382427345769354348535, + 51502775126011571980254586516146643923, + 120902136313508117728022589581597092354, + 8781659491589495617108668665378663746, + 47279084473518830506271367116680954263, + 60377115771687899021759697105578393304, + 209278659436250364944289323786947160431, + 18442303040687767272347204109349832973, + 183426148187459046204931942631981048151, + 21204702220791217408580219923430013058, + 209278659436250364944289323786947160431, + 18442303040687767272347204109349832973, + 183426148187459046204931942631981048151, + 21204702220791217408580219923430013058, + 130708943470798672986372576766707047124, + 42105336567070216007530935218568166276, + 18409785247140001260854968342138874274, + 21805797944095417085499714179751915747, + 40, + 328698481921488058451622279994088045371, + 3353696387493864763024318537033673067, + 65022523231759933401257220146351621318, + 60970068225689312278837738948192366321, + 76221411869249609519457877003353820924, + 1641289655295819700193941052275052010, + 137755086143902799784696525397114631191, + 40278955122703798985527944790359068144, + 268383815255598178490293124078039445941, + 33271838831784851375897478696297660103, + 22838179552550301197262817129488186019, + 24370101379360917232378047091252545055, + 55920506037449683606055378638469006274, + 47774472662728975626968269907322288290, + 187375084643533102606632664631589489683, + 24639351030622595231212242448205003546, + 256073447586644173546741519984340932641, + 45863396402348290971563879805471793537, + 125724145640004278881261277033983645913, + 10040634335232410863877024709220013973, + 185674108869777185737647092722902476393, + 40108415426238846590013902963171622753, + 229162142763905902883396545920713395569, + 50743378581731995665671440804726630110, + 118673910409439428537695648084476431958, + 45405193177071280682274072199438203860, + 88987629148225099718223150389867116241, + 58289232022786295720684243823216131410, + 260743377834925437539876344096102327578, + 5516350839460832978061597680491830185, + 297004817979822802511725658217851233171, + 23015509357423753816571469039795562416, + 267375021093273359198069595984936314624, + 12184370366793422203948559187015228658, + 8796149974464718398942315467374482021, + 24245128368870451822462026461445909858, + 181832610823533896215198104322252348046, + 50124102913278473787190405825536345979, + 115158107662982051917629881517976046384, + 28201964943873472736770730572612681480, + 78969760557087779904302837404458147799, + 26255112202466991885152485640477337785, + 177009677546869149796406334874634192655, + 41355453799557373025807292830246379821, + 45513217818285577138461615320659179349, + 31651824172428526151857846242845136146, + 248918212183720795349246479586075736323, + 29330052083192175301146938566809836284, + 110321074391351602993039065434787656468, + 10032463181655706241849983618239677082, + 325154388469541972516050718362086339810, + 49012448292328714910058363103403889612, + 90807713956239504349694617135254759989, + 49424699063478037134967122110881898801, + 297498809151245881886605783830998118981, + 52779357285658511596103753782474628768, + 225240113469631567813856117772372607358, + 50113824552817747464654792479639145104, + 295476659128625428012899637343388218136, + 54099492416632018305489782787496016978, + 282276526171777368244776417929286790469, + 6954949892905402655530082598101291629, + 109498105940945770215749911757316902122, + 13182161058442523647854699271227420521, + 227213007873435056136789465629519295266, + 46508389923469483824628226510187525150, + 312486041303480402811259089674714080605, + 8214707371113254035593618489851577129, + 303323559438765519778269341516421455413, + 15455421158224494497341134243880096641, + 240105866896617655532163995819449888335, + 30234954971246492332960866305470177016, + 72899936833412020103451459348884184946, + 12031092765625149567357354212985899196, + 283239739748023185129581463300068186421, + 50812261644711658620942945903964903103, + 131298242585163031688347661029035876768, + 35129157977138842865462877714160665791, + 144804154911866756490453332791185339222, + 60576200100326092142370376420260671364, + 44, + 256778038420526605403385553419710403827, + 34594497995634050238904361553982521677, + 156125858768885761082395274823269608777, + 42632319655626746364397879024763251675, + 324463155943839498802099161599435083505, + 1896117274098014738575588689593322688, + 173739454568474136097156809398288546354, + 39162825351881320889124347331106343192, + 150712443001988168627027679936992707185, + 43130268720258442543871846394712971506, + 124790928427636451511155669784155791303, + 55003843775456834526135573005117287059, + 329194079032644699166679219634859825755, + 38200919410055183981120663637853219564, + 806555065640701193361911772338270356, + 44652569205219493944848017217597964564, + 67690272836406331260778171914576615371, + 22085601434678644491718709963624453931, + 15787375329197705852057438696742520521, + 16433166360919479670787872296817161658, + 321266169961586904475390634147357015195, + 25813363330765820463242566802924995259, + 115627737809000505585942555338085754427, + 13187675944422406371342636757794460005, + 98419306585635097262366780306430751162, + 44691232251043659175213748749504674164, + 1584464039122863748814489403267906329, + 38206871549317500621270739124922465979, + 3913480713371771322406703081942631497, + 50210000966821503555251640950230591007, + 227592159928877566660221015387215400548, + 19657187231256981586403306012591879698, + 224596552690364910504351711985965156825, + 15268264844548701423998855199595331641, + 292163641999597760752199591911510263294, + 29679931572357955644112939816553250951, + 136785563860071003498918425209698694415, + 30801494443070492722825064803355647757, + 198102597193278973086934033330094864304, + 53260373388121859915206165436818937343, + 184681695713794302053349359760964706096, + 15, + 264108429001303722427802870724093986279, + 13543971730507892029928287723970073464, + 50737917675241462435841865806324505524, + 57217499220229191408105256897207313943, + 319187872014451128610744985556532589481, + 40805437137632778662732614454536892046, + 337089740137240090269253244771930045106, + 4571499110366805057206297833224562353, + 228216804491052989432338459807010224875, + 51772381816270801437626433100830917103, + 54462396064496612915238673629927640810, + 60253269959438843438695407614975533264, + 248099883731577670332776555257323675684, + 32094560923584221414196160298899545839, + 183010516484914532286074845887023230676, + 46797651136869666718821106871676490784, + 147131762440078964285328285522736380072, + 6023302647237748959182389152086213940, + 334999981535680989679075548695579702416, + 46867886082984820178103153800242345563, + 261473328442269107677921316511202388098, + 31209399390818073183927549142055184347, + 204829579094245835504072214644629994142, + 38758170963504004425024146403515978663, + 87870392936212762380753402498325990575, + 8941278212243844697805088076488036477, + 87870392936212762380753402498325990575, + 8941278212243844697805088076488036477, + 139105560804119660102137931533766510653, + 52878914526249394042423368468201314453, + 186314311083838492271461338135426289068, + 29409146073251795048626586429447250615, + 65153089436549630521843698613375678796, + 20326373665322588362523794905591473654, + 82630250488140220135164477995975453071, + 21653766630099316961344685650213611081, + 240615263710994159588825607406980432374, + 2023257620318656882195083456718684295, + 191241678316435498092039508129704766074, + 26526710294596803814743299146860848148, + 200023841589166845782527002032093726183, + 32131907903732432268154412431053318338, + 225665781081115660956455975712602927425, + 22462173944946677822229017898252171419, + 325408830240605623233786313222726241454, + 24999595901708505792652656217479230327, + 4, + 226061222110708937246875070503384197014, + 254238326539885347543025864900118907, + 125627980903682053265906395761716052344, + 18840429028465548108131569055289410041, + 205107191129431575963601474475680740265, + 48857277157352362453663663144665542488, + 302094354312502933062320852736962386112, + 33306745535777741109751990657134102268, + 150268926162490384516342106297468680841, + 18329506511484049800212705250518952301, + 147790792404843779500643036393678660332, + 45428612880231233840236750145606737256, + 187418340559402410776692509425578792580, + 4411175188735141998916977293299181478, + 203777974510174572463952795548674932464, + 52623372021833232571129617951926889905, + 5, + 182349676848730290555373700591495130172, + 9099500698528892190140402448916837840, + 314343894245451252015060857073422529697, + 41386320927751653538146997029566792694, + 266550142055757366945624786919324688381, + 52751055302266701967770437777994472946, + 314826698853778751040688060128992892785, + 29526265664759111461162864932671032693, + 297853374878955042394439182562356067437, + 18943381939169976938500170016400771462, + 51132472325960427824055442901225401889, + 40192967302049877649090870652016225230, + 180481603886867200898847185585683865246, + 17369276763834551630655147761599682184, + 60700741077581536312076530794387500107, + 21812131526232908532380513293901122822, + 170049424806881436518799737876377747611, + 44419917786258331948835700999023493034, + 18706340714158587042206993346, + 32230547309433626746678094691, + 611285548893924000, + 0, + 33421300294602642022167099900, + 25334174335447721721499741001, + 37747904728149875, + 0, + 28936894595215943060530427291, + 45542070747560789027413836264, + 554490200872627164, + 0, + 33834633319460206590447894351, + 40454374028007427938117312117, + 1474554557371422575, + 0, + 55161387404846485148964292164, + 5823119649961870811284876427, + 2733606661188409060, + 0, + 51872375905258998279994729683, + 45773568889109945329012547261, + 846642851961600002, + 0, + 45, + 17467155484720643099181060094, + 13016663650269136881925242440, + 3430314887222795216, + 0, + 31289273975837717575380348928, + 78529004314978926330996661975, + 446329830054575171, + 0, + 11188823191605763962266814438, + 57961612791846472500267053728, + 345482338868453800, + 0, + 73749572448154061554579113599, + 6489930896103721881239875935, + 2960141138637881065, + 0, + 54431380579962524829157747116, + 1313638361283607915123581747, + 38321833672705176, + 0, + 19345139865241319111897970346, + 65119870107495204566599272438, + 2998493131503078134, + 0, + 75476292254500384589861500324, + 54796634967338013026243522146, + 3207927423540910663, + 0, + 53972942421940295715826455687, + 67641902832098868622940039518, + 544717285987670548, + 0, + 53509832721730857315047072968, + 2375119243892714962306726802, + 3179448481964250241, + 0, + 4952135245656397362750207792, + 337893492480061524472479766, + 1005355737801236207, + 0, + 46936915540626796482085819590, + 70670534678734888692003863946, + 3385775434185752923, + 0, + 34205336690703882215723493362, + 77375776232564951263117980388, + 713180341412960854, + 0, + 15887754839595542852609212459, + 29482776362275677858709049190, + 1942515785139245411, + 0, + 72212328209722447545073666914, + 3129412235434450170186224273, + 1179062496982544469, + 0, + 28067548777041589144213468243, + 77408951799128363506448489922, + 1760558459898555837, + 0, + 54466862337887098639473739123, + 18443041754220698559468078059, + 1093379163347806009, + 0, + 9962691411227736094938344502, + 70401459769591404204603551939, + 2908651066209776583, + 0, + 39434136634593413647122886271, + 25420980784761271579300600359, + 1513107097063294752, + 0, + 72073490829526740270720514361, + 12026227534310241621724169665, + 188470941611245300, + 0, + 28136504250273418687772790516, + 63381883478242334575306872575, + 1673240833061635987, + 0, + 10711056102621643331674053008, + 76096726845659185251931691988, + 1861984457856116117, + 0, + 68186700283068733561345390262, + 57483840893059347161222079130, + 3207128202432662240, + 0, + 66840644857283339361775026901, + 1872313378897875911650245223, + 1204123425589474161, + 0, + 46181550495312706669527653519, + 50589097043925781930438005566, + 2163065619283314296, + 0, + 74090422461383606872579909054, + 79156717537172246401028983168, + 3439896244547359262, + 0, + 25506585329540615249637188629, + 77725440911940838188260465948, + 3475261147258120176, + 0, + 34003077123452019214044684855, + 47717366185945143171441899368, + 2595711383713448009, + 0, + 53570056685142855209201291380, + 5917449299368828732712574905, + 3405454610885571107, + 0, + 37546772932776051186948640344, + 6816087642598258998512693459, + 89579074539862654, + 0, + 6504327091217429743546256581, + 41052670338097580142381581854, + 853147424669801531, + 0, + 26217036477777947724694115608, + 50176062460617292487362130713, + 816751820507796726, + 0, + 26671702341339463785210419784, + 69281787861859647125119381540, + 136354054840032334, + 0, + 30063456135512342202220310807, + 40176197107507868928423702283, + 3480653044749025214, + 0, + 5856748290970483594846472511, + 66564775071233928858902599439, + 923406017788610890, + 0, + 52917471529021839617697947322, + 38328577221258618111806634739, + 916881913635740710, + 0, + 67328883382534196291903440435, + 59301719649612992513010960999, + 542414419911265807, + 0, + 18055959320434228707049431997, + 73626641466708785635287692604, + 1085861960905080136, + 0, + 6058080434634418048054231606, + 51015695976150672481361130514, + 1666455227064229871, + 0, + 7297836465494768702590048971, + 3480690996878767368629591946, + 1184024694632841499, + 0, + 64304713748819163087811337751, + 45940332514946519105251659038, + 3437240325175831124, + 0, + 41676339474673603035070991612, + 74842129661684416962123410433, + 3242079057124317037, + 0, + 10361187448249979416900757303, + 49906000682464705689226110019, + 1887837509715916364, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 46, + 66504116520623289427172218529, + 41726294022278302439148314482, + 3465948854557950829, + 0, + 54633749341848515859819197054, + 74965910895790038689615963602, + 2773444526955679871, + 0, + 76796556396311266489828561615, + 28113936902717640582834704670, + 1787110416396429021, + 0, + 14572723001322497206430159742, + 74722322700105196969948306126, + 515014445076269730, + 0, + 11225148756989135693078745119, + 69119552742013659515630964226, + 1806448773332827250, + 0, + 4079272140624871966831120075, + 7380335233379697525878751217, + 322936544018945587, + 0, + 77115726185659873432019794989, + 58519244759592336994515925462, + 166628824805290046, + 0, + 18272390802278077396087880839, + 66224648554782460114246775614, + 2142488911672530825, + 0, + 50214938887699682190063167912, + 43546043354993323499997186470, + 1009846286520447755, + 0, + 44993160662793441291790751545, + 79195376808206741010606124162, + 514747966469917898, + 0, + 44311348507070687864800124738, + 45727032113109935561667484375, + 3115185535943501734, + 0, + 72763587513738474664963921747, + 57834911579463652275727282814, + 3227858000424058965, + 0, + 60635436637475260353755640351, + 8131647433994412329318045863, + 3402213861733387156, + 0, + 16164975608926972655668504425, + 41800328891108411558374040313, + 3482877088465664522, + 0, + 22461582823912623938891438969, + 21323248927842365843929878632, + 813931332863668833, + 0, + 47802864316155838362964538004, + 22596288109406265813813730351, + 1860273472283823081, + 0, + 72716853985106301567242458310, + 26996827962054518337051904202, + 3002148268052936963, + 0, + 74646800150981352078350792187, + 39394539222264052281598881482, + 2989153980190100601, + 0, + 62359627355188632860162938032, + 10492091999120979999573206547, + 182146508539445789, + 0, + 22919769566977403993340299668, + 6759776964570809262392857089, + 2081520324930129675, + 0, + 64069627949045361700574157242, + 17105968595674177028137934922, + 72543999709238431, + 0, + 2628003911494512942922974445, + 62743816586769375056942931606, + 2005334765950458168, + 0, + 6123377612426893473791896790, + 50492103952877064745946078011, + 40943783984644170, + 0, + 24968238790234241868714298143, + 70583414542967036691985731585, + 2751952452213189706, + 0, + 34462514606362626109347044476, + 78062785500238724686214726098, + 563746373603793770, + 0, + 18743514334742236388606705993, + 5586428166889893977236941731, + 415888534194353333, + 0, + 72816166218324878081990313531, + 32918104894087000544622360465, + 184850348843980334, + 0, + 42895533235008558765731982585, + 4922027420366210282570306426, + 3343057449426329149, + 0, + 11893286862886515957561536213, + 70337974998537511406274894917, + 1355723147910406387, + 0, + 26046169059118638058744511848, + 32700440733235848525210637220, + 1730769411645832135, + 0, + 1116741604890245979486655568, + 34481724491905856497379468654, + 1085216121563438945, + 0, + 66574403427321543282445620982, + 26975813917206467417189160048, + 3161612348582883970, + 0, + 9142609684604755108373244846, + 68180114860638817925182346306, + 1892821747782447585, + 0, + 55298934029098285501650914561, + 72696455573667576024124690317, + 2699373967232142629, + 0, + 35986073882489776823821759832, + 28275736362367488669836232673, + 1972068623459486733, + 0, + 51653363404991252856841560898, + 47466724467156738016325955903, + 1738073421819036544, + 0, + 68708716993363342576236386863, + 17305691196648078037284632221, + 1452292696972103722, + 0, + 72464146758813300590378981468, + 18742380691966410806743824340, + 1657423426640537401, + 0, + 73935483213335075685752711985, + 15222136133969349677357112033, + 2804945226842745630, + 0, + 52025568228786302368096979603, + 48942554799284467474711826278, + 377485355048522515, + 0, + 11112185914269147514716080991, + 75575080629895408672374267686, + 2326573018753672964, + 0, + 70143115726586670092917928873, + 48597286641794089084760429844, + 932343457450133606, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 46, + 58453460305644801599242359982, + 26562077179044655620355170485, + 2612740968679540841, + 0, + 43737507095394702064867430399, + 51671420187608657308143872160, + 3421429737981934731, + 0, + 44146299510105212251179904829, + 14100861305934268247277977349, + 581642436003426987, + 0, + 62579839200546562798711086641, + 35189428884102190254547251632, + 1991271081520893881, + 0, + 74154649824990759693920622061, + 72324466887477589698258972142, + 1435659724959689828, + 0, + 14400233366528378151241776506, + 65834272925179443546872755385, + 2969237712049891092, + 0, + 19450178093553717057195464373, + 36968908176991216443584930041, + 55707286897577404, + 0, + 37131754226676991034953419928, + 54496904957867628619488282830, + 2514136374215507268, + 0, + 18670036331484670525489149103, + 39659419674766573283163102771, + 302756983866820621, + 0, + 68664072151380355502602713109, + 17826995532372760278350577335, + 2494461967831776780, + 0, + 4150638671962562818908241255, + 2851428245480218638084067028, + 1949857462894120104, + 0, + 41977355932819505122716822968, + 42030974738950987039374999185, + 2557021021037970515, + 0, + 23444477170820234599274298107, + 66710245705032854041625958639, + 2976861880567299826, + 0, + 75941538957293415786155140682, + 61174022310507512062681076012, + 1766054442250749619, + 0, + 41871611448183619404783587927, + 66798161108422650229300102893, + 1604188806553988243, + 0, + 13962759330709859216417115911, + 66016241806646102735857519817, + 1253224491420894537, + 0, + 4632998037841530870304624816, + 74245745143650519149820311747, + 2765987906964611525, + 0, + 36597061259267699884718187661, + 74926185391617036984529979919, + 2979459650348357927, + 0, + 16580541608334482489289222821, + 46819082081014423674405242579, + 1776865721953566087, + 0, + 34899689510494759903039305455, + 47690048160555000070759063858, + 1069434343444359315, + 0, + 76442365440190087465432506428, + 76698527710893641427341661389, + 1854196109021901471, + 0, + 1366709257275712263148187582, + 47646395168407515256829267231, + 2237260067879455464, + 0, + 61217571304275501570986169155, + 53466645189276742718058773869, + 3345569546913019701, + 0, + 70433444176017234735271937900, + 58725667796307895787428058709, + 2565969724876368552, + 0, + 56821497067202177163079168201, + 20490563684018078615625086879, + 429543510760777482, + 0, + 5911720041517076147751014822, + 62888069163061624749481767132, + 3195812873636970267, + 0, + 36424524932939106602072901380, + 62371454070135378033145459274, + 1315293718460169794, + 0, + 40798135237201227562473294210, + 55143728270797040528506717198, + 2416464069833708047, + 0, + 19838744908493439767412138954, + 40389828448202860486521724878, + 120498770558919030, + 0, + 5653016252155980122672217091, + 54430701377946620779097861312, + 1934177886747919972, + 0, + 26053285839709844789092946637, + 67028136349138000674204514765, + 945201733323691489, + 0, + 75252522450729341498478659610, + 34030310187882717373475041176, + 2968753451347677116, + 0, + 67671616482941028764344065244, + 67556412460462007276351179448, + 1274582335919741959, + 0, + 6459688414943570598227328191, + 56531161753828615476874865808, + 239692112540164599, + 0, + 72337667322511340704285843303, + 32571689331079615450634434092, + 1731464112451679256, + 0, + 54680625929506419939831587912, + 6499291583425845195004090378, + 1234032937839152273, + 0, + 4426298118988252195893073184, + 65548624860320173910456356680, + 3231983020597131840, + 0, + 57970248841545399052485250439, + 34268728431486399035995966930, + 1826951211307596821, + 0, + 53034512569628870276795576302, + 52180713661386621665294071770, + 3168617438764824023, + 0, + 8350864926637645183153204131, + 14572426506225051458011539495, + 3077236242072888461, + 0, + 54222973368526094560901681815, + 24026335883808483695518932616, + 384718699762585710, + 0, + 34520660579283098654885605503, + 56218233030721293045079772784, + 2320095234953127879, + 0, + 34609143527988896471668645415, + 25604063374457416313021502650, + 1404140550639650098, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 49, + 55636174722826708334415448405, + 11094311901588428393685403957, + 3423850030067911157, + 0, + 20025073186502387632356383980, + 31585000007859299551544400981, + 1346337047261098284, + 0, + 39609337998015713105619155750, + 27299525625529682286624344267, + 1874332982386316398, + 0, + 77898279362201369869684069332, + 50394652011441978700025562443, + 1523993922983789356, + 0, + 23661183288037100585498175565, + 9783673928055863125661416118, + 1218794313348220293, + 0, + 9806210303921544796777971504, + 50254942602856733160470958322, + 2755920048453265782, + 0, + 8235414015509104721857693701, + 12595569436089195172864231507, + 1014900919492139871, + 0, + 1394308839044545527785130790, + 74480765726850223341067801242, + 1259918974744478396, + 0, + 75495926289459580943476673475, + 58790302784095330432326360292, + 3352475403580288853, + 0, + 53638883145511522120304148952, + 58420887641419547245702446942, + 1710872724215043743, + 0, + 54234417836321907460152369784, + 32278889646242548413609919504, + 1027050719094124032, + 0, + 13077357913218197467531147212, + 45923922845514561941539725679, + 232425487383712655, + 0, + 50699289573786651229177836993, + 11691626376584597206464902853, + 260394751261167370, + 0, + 75062419360947709895013703080, + 1163538565451851150296078, + 3102822000931583305, + 0, + 28596167308822597711315659895, + 64762373280367510345637148966, + 2182653732212094799, + 0, + 60167854746899615495548047181, + 41064104111231068440543647763, + 2009037744978915068, + 0, + 58115356562813306233516043750, + 30892120043666585777434394021, + 2028327092215563415, + 0, + 23297645923549182633298658012, + 25422296429388043764966983590, + 2807396739828029306, + 0, + 76425421353193061756365451428, + 54072564106769205812533349994, + 2406712997902160448, + 0, + 76828150361259691193706100468, + 12419751204784804794014886315, + 2272712709237384657, + 0, + 29171196455324424399441412905, + 11484282495022245772468735915, + 3206785979317815895, + 0, + 37919632927282760512153232944, + 62453094162541528114978281284, + 2711152539587849629, + 0, + 41289902404258084414715990038, + 79007926308937665906687140786, + 2204351676884062186, + 0, + 74326331994969264953159794825, + 35543479545064470586791639851, + 1354404822742866220, + 0, + 73691541568192980094185479450, + 2205400462069296873075489147, + 209575619958868816, + 0, + 62353920616653602639612014769, + 67251388453546746677656903204, + 1288609386567704169, + 0, + 5732249902415863334053387728, + 10881404196699363138764912311, + 3306503498745130710, + 0, + 66177095824220068876207106962, + 930175027731974741830286477, + 131923921473869221, + 0, + 22099368761012373084512686209, + 1101743051350513547093956065, + 996059711122601831, + 0, + 39402504719027043487901270316, + 73977142011171306658374502381, + 1890160316978506074, + 0, + 13921751887289885527413320866, + 51324915813460540312828942643, + 3111707547313675319, + 0, + 35416316143418574681018563554, + 59366724016551532865747016163, + 379565393250146301, + 0, + 68054148302418418623850939876, + 43928052635641485783454186311, + 435238121387233561, + 0, + 23137368853141942537338192069, + 59258358533397768052450049780, + 2209341489653925503, + 0, + 30656450235747713806809693296, + 76946615353326792096482268914, + 2103821685355402838, + 0, + 20226525060535353731796720358, + 17267555582598215456856724190, + 133045479633615888, + 0, + 38320747655880815689714917632, + 10528958998365331212219047492, + 82255524542512465, + 0, + 62598182968011592231092175141, + 27460593355643481628187935950, + 3457340636578128271, + 0, + 50355475691688982373454539335, + 58276725218082645718181702850, + 3178912568741332103, + 0, + 66329096651193574493439425000, + 5677030565972805274332210385, + 2584748762117671269, + 0, + 41152692176841920780969967595, + 52154889902141820306563137572, + 1663145949295614964, + 0, + 14356336687912662007096019173, + 24743548462464039876214681487, + 2114977332390175784, + 0, + 52025568228786302368096979606, + 48942554799284467474711826278, + 377485355048522515, + 0, + 11112185914269147514716080991, + 75575080629895408672374267686, + 2326573018753672964, + 0, + 70143115726586670092917928873, + 48597286641794089084760429844, + 932343457450133606, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 53761953849680527109314185629, + 49000714173266086283554743089, + 557238757526462219, + 0, + 0, + 40623674964915492078430425587, + 70781984449728296499447166856, + 2445879813046935881, + 33730729557130207122619604447, + 3790975713751805407313586223, + 9678132143984984, + 67270790906601643361269422768, + 2835909237816824875138267714, + 1378910479992043212, + 21226344962558265092813836753, + 36295059058268937806869017560, + 983766261671147715, + 71232696067878615817208609554, + 58756831199636539394632710997, + 886482669722196252, + 56394639267030683714211089711, + 19246315922069064775116103573, + 823902604194909376, + 49759726950113728291184355219, + 51490771961706471654644324756, + 1965793615073328343, + 24121358098579829639801304209, + 12881143800813403708328804342, + 1265438707629183398, + 67806201761652337594461989851, + 19293773346573038590713927216, + 3450986893890874366, + 9632679635886734366358848531, + 3208518715750714125849774466, + 1925118982703669096, + 70664744356828316980067228658, + 65471667161496017484480931031, + 2747802809013117492, + 74584681842793770629728847923, + 61895670807396880906618219871, + 1791175404445329166, + 32582460304848288106963487295, + 43961818627464749680137138112, + 2263206840826264713, + 76376752723533243228947266198, + 27610309007629679948322664625, + 2537586912635899516, + 27579533347873532598350688726, + 78137418600429545673469052373, + 3153420653147798837, + 26280941166026713073040259545, + 13156487225316839144570821157, + 2543518468704067359, + 29976897583361868383575767223, + 8030199801837395566747405482, + 61650278799928379, + 73159840577666151667023418362, + 2486635556691473599059814530, + 2051634344701757301, + 68372581730534290483094654959, + 60930935061907324816933865335, + 1533503335331634562, + 65205589824435443325586533521, + 41739860141348840804820163663, + 2095970111166951276, + 77548101788552680065253127319, + 7990667888724641472585529848, + 1965613922919107234, + 54533221978344063572547350393, + 72401207593890803818036909493, + 2054727684858446670, + 11455625079970445171222107790, + 53464439518400230745799869963, + 736787475349426045, + 63135663946360461190487047964, + 43652265147908618133545963519, + 1879669650136368065, + 0, + 0, + 0, + 60184622296806572439997666907, + 75008714795759192705677030670, + 3262785094589981400, + 0, + 0, + 0, + 0, + 0, + 0, + 50185732680358774463548804626, + 10607397758555418006709821705, + 1206124784999809577, + 0, + 0, + 0, + 35, + 64437268658247417935140459350, + 31115613872603716876345914420, + 3392966163349305812, + 36891812092230241159323506941, + 37347280386410077088075964148, + 2900600926753145664, + 61138076530465290119288259214, + 19482074463356930196619801764, + 2455304848911315747, + 45309882672010806397129233750, + 12549793137892735339153798810, + 1456117641636390327, + 20760122593195371919794436734, + 54363429438396492500221168644, + 2500582781913195466, + 35546803736507182938990356759, + 76932020603569158652657071701, + 1925710361329419259, + 77381955716316316290747039940, + 68945307510187871758927082649, + 2180670374843283875, + 74956256814374872939176731986, + 55305844015024945815516254831, + 1203842257929187625, + 60726072098180342359561628975, + 53282642850098385178023377267, + 2175540259301853212, + 60209412226349083771710498837, + 47779271235324246374405564933, + 988999879306492078, + 58487097087289780995370430219, + 42629913443175157891284247791, + 3139892117083308595, + 29157342622201947926682665382, + 73219735391219726390030122365, + 3298319642409391690, + 15763457315045256262408571606, + 62557136768631269950576820898, + 1580681241528206365, + 18262037029709175593049517230, + 75081252519878528774798297557, + 2611775262901731138, + 21179434813102658248726543054, + 42542932982324853752497343652, + 493693129050713748, + 46521592617046585205176039184, + 23685098394450557119739860216, + 2755794596655965784, + 12000549891328264644868522283, + 36890206295415976285373640682, + 1511139015203500303, + 8927777310445691165995027953, + 30409592213448366465611986342, + 497979042676735734, + 28953696881538698762408362692, + 31324855410334493963092148361, + 3474755881682512925, + 45594515827956543542638542710, + 50141484382966597726055439942, + 369671941776598423, + 32013990252399377074237197981, + 72968052858246199652583563562, + 1605516084318216041, + 13858839227515112910874279859, + 78519643242110336976191201946, + 3234727970708116672, + 67210488746451003255302521589, + 10562530200300945936246641175, + 414394697127515184, + 43148689670121135424926783061, + 2024394585410333727471433095, + 2162354490599648994, + 4385806802716040550566497530, + 65583516083589680381914903572, + 1629069098861565279, + 44332658321019083942580223261, + 52768596720272375496120513720, + 1012111074941124979, + 374038383234207685017107800, + 5090552617656711572829671052, + 706594979944873401, + 14866285184051131498573073967, + 48134308156432247960466861348, + 2597758589227886011, + 71733192333359590822976695900, + 44847663372955195297804623728, + 695396508736267559, + 51718986705051867991184498806, + 15311910447658503071720021152, + 2672187172417978710, + 6728552122817237011594776502, + 2176978499757141577640060358, + 542111734275855108, + 76065633239015545547588000019, + 25193326412224948901784554220, + 1502017394927824040, + 24080957826726740440586062047, + 4443133296448695048332475296, + 3426794129344458960, + 22179100901076160436286814315, + 70499874277863761061324653449, + 1712581975905148161, + 2221277148825686480130143631, + 1441914868875326152126278908, + 2756080981011748004, + 5497675673871990030631017619, + 48803551037174516818062975860, + 2557166800823837044, + 66896852904345154459469198432, + 45765800452870234133616011541, + 1500459634258672712, + 76331030751353197616134863913, + 15857915712076536168045946938, + 2729001634848920959, + 72566763810379053709478346579, + 48874388937710456991177291035, + 1332061368541379415, + 61284250959959867198225589759, + 27704127801859480888067613295, + 2205507205130871760, + 35146456457492163297209384601, + 73236778816146392012869076098, + 112679900724898428, + 50458179616858298598112374593, + 60633262116151642274201231521, + 142247295459396860, + 49045360749731415021810859040, + 31912419192019994081672553444, + 225168302942678649, + 28032308257752927750265983793, + 63204986603325309948040082533, + 1944127516796655434, + 69109991432159597262317983192, + 27239545591699682673820198783, + 80341191683893568, + 29678147597193288481925960235, + 74805492386181824530563819921, + 1857087854104368699, + 25981652778563595396191341082, + 38505653651149817914171257967, + 1531996739929069021, + 73037055404320563365108375626, + 32847624207334467085728274331, + 533521512125952616, + 31586551018460148920200238648, + 61454083793939334534260731363, + 585055644597814946, + 30910644855858932951732741923, + 25713470417291624482423793989, + 2407863915621093082, + 45077263006636187703015411938, + 18159905762244418556719199395, + 2670445227590160187, + 10730214352727026853599422008, + 28320779204980434598156142536, + 2063957377832028259, + 7000039148142951320767622630, + 331360315428794854254794215, + 2010954230119060125, + 70153080361911769275950303427, + 839651557264107131258845620, + 662831620366924409, + 12281024573379214161882786320, + 1781574853563952750760532074, + 1043740813651734222, + 28718257040077853397893564579, + 11100036141131336009442089381, + 181769215117535830, + 10611527101531725572556278080, + 67643600460073860547621565649, + 2073932238285572202, + 66217130488495555772296692193, + 23963569609420642853207512367, + 3275141727378661064, + 35491877279258113320483501737, + 15490985201982002746465299695, + 1815966520146827858, + 57712244068711697368875877328, + 24606165294881519873356265349, + 956604234610426356, + 52275390267524829426874057929, + 19509798217238573796305335617, + 655437658151032629, + 12954162820141696247841058703, + 75083331181941874395278924447, + 3468356488316517726, + 59346371256155604614365000367, + 22230072556027693757439275088, + 1542705877544327868, + 29336349308978655827340461653, + 62887446101416753716002725049, + 376731253455373139, + 7450939156560850018800377799, + 43544749307823814283420790080, + 2111267936340933842, + 66279875193343448983929296838, + 16355842610138061758459677306, + 3391102588217173005, + 30978334183776781494343329359, + 48113499295808246507979969774, + 1815896118488244933, + 18543634238399976937022283264, + 43949636025466584327041817827, + 2291869079211967115, + 69289445931089458544156799784, + 63615892091324068833657429587, + 143838764027495880, + 12978651237195334549673511887, + 63818425042718955522510565647, + 3299532249052222780, + 74017504922852972905435865872, + 48878710900406520468844166025, + 1556885557494883753, + 9866505880457566805538023166, + 66105516565069506132949894664, + 3137586665893319320, + 3402021169490443042007784835, + 77658898202630772751495722308, + 2618550293569752663, + 22181704306209197034415828128, + 53950993725999155771761934842, + 1466039236619111914, + 39666714051201049301701728971, + 28058805611853938174741478684, + 1310708567722709170, + 53224511324042239307226429454, + 33224196761352004313812341450, + 1313498650761936026, + 57409112404575855137687481816, + 41559693237758361751513370354, + 3306192303062584459, + 28914040348610010769604245333, + 228276970299391625379639820, + 2277217990975668422, + 62696622372073486427795241736, + 12323368465391641953813300312, + 3466151140675328989, + 66967229948377093647265193630, + 3806124908796655855912227329, + 2579111261692009611, + 49854811142335685008076425451, + 74699100963195883306362776128, + 1312947364656363694, + 24770719609533755683245828984, + 54137818385284328469940148520, + 3384258898151738931, + 66770113720369110124600555093, + 73364573514700652949137191361, + 1902459979618860601, + 17564152025640359717041848411, + 37777716313867768566945548033, + 3074718865545811647, + 4384702442421736376029480482, + 37993306516497953943082051849, + 99127300175159808, + 72343791975016317545863535710, + 67715685529711428398665017022, + 2096224072112621167, + 21935917565974467084420001588, + 78528079955899031047439026283, + 2793919397645151165, + 61448686300179546978748168708, + 53810650292473862576258179307, + 3131985467499627737, + 68212601731556563633013577975, + 40463832628412200373919775339, + 1866020423364492935, + 49317326839164123847306512197, + 62119135667698400255024116701, + 2563301593803869420, + 29069005519153375775067891890, + 6743394028014066038071737504, + 1482869442356487291, + 11477142623500256244563440493, + 39802510278140095104431917753, + 1280444737861957945, + 31641560618786653539487114891, + 21189543946012340832330547420, + 3313757733206799272, + 72009394617306698537428426097, + 10346690143762538649834997903, + 1916104237429226403, + 25807607428919190333587311611, + 76618266534486089144825317987, + 1441549212841105731, + 11803269006731295059891145905, + 10830732446134378563880740615, + 2961508967013401216, + 51009237282155462134673146529, + 37184419592542607269851239151, + 1272991970915153136, + 70875413647052282196328396742, + 63179841582593558759778233678, + 1740191997589851010, + 3608718810383671088810938312, + 27293690637431534744442789627, + 1572988093616516327, + 6563734098340984461402530126, + 31106292121024636889161522994, + 1604612659025931661, + 47434475206848772137005042331, + 11730825198228431997613128778, + 623536358139180942, + 40235829119084884905494357036, + 62285411392493923219802482948, + 3277498449212013287, + 48960685379683525496123514352, + 50130413986386143928121719871, + 78356227170879168, + 346399032574586963724263687, + 55698279925130558370269028880, + 1774254669835274491, + 3412121803506062955168596126, + 51063917676434287769553612308, + 2219027770901661000, + 50016578519633875427344317574, + 42032442617211835846358088692, + 2857953188894666738, + 15419263309683270056850917640, + 46499570687409330841607104644, + 1015232710560092863, + 12255233281104415203888527998, + 20121869273940617553828891659, + 1983599474383470418, + 6937908618715895444802206769, + 19378054462302002063216933889, + 1963419431418097598, + 37646816919233891995002621294, + 57199515680340696175756964673, + 3323589971638323477, + 73968326824951544389726305784, + 27376052095205683156962009626, + 1435809975901717519, + 32881407499221555688130374797, + 68342936529603074287061768781, + 3461524732406375382, + 31568548977712633746856248067, + 65111622067138645480043371203, + 3367758382654919858, + 43303610237443765979668194430, + 33314241971382104057886955060, + 2581997166408491035, + 63131104534259139861497756451, + 36649413223121159108727177524, + 2823324950632692909, + 5979861505025630158064739516, + 40643803870712016932128869855, + 480622967347628579, + 58936878155687291695464638923, + 76835204907067721982734669691, + 1683930016689634751, + 26222236891475636046766826953, + 49942148719264901287094304621, + 3378723231709881707, + 47647873396269220157354612627, + 63277214428770255415457425838, + 2745169786157116149, + 16173365381981434642429428850, + 46391560754745297199135748834, + 2611360484495822298, + 33469849811964432030698163324, + 69468148282095050593991424242, + 2047516420342413975, + 60021747469296715440005940945, + 75662741590834665264033291161, + 2444321799881292667, + 18147743378007993393013033979, + 121133667884874833065888349, + 2827689307190609845, + 41123753779211892521846338716, + 21095676902615715507911264829, + 601098671400575989, + 64015490712163006841547823174, + 29583512196151839311748677554, + 1262989639274238233, + 56176613110061787631980493733, + 8203801037129330726264277147, + 1298742671109257972, + 21210454674197692249098896135, + 28865662031085092216634691861, + 993917974740939956, + 25442874798407619868133995178, + 62997449526268673187793561233, + 2579379152660244543, + 78832897385708219890316140806, + 9983523801263807266148615268, + 2926090631522422194, + 20474182795930406664909598195, + 17885982149876340451281283234, + 1080824455199103215, + 75951259762915644077737503498, + 20095453509288011600243239701, + 1920973955534046355, + 72389883961707622943146969131, + 46560782963382732709300341282, + 2626436812457683526, + 63609939196721264801279209973, + 41947911392055203686328412202, + 3053188693168857299, + 12564808583623005981673393453, + 58976326280379127619209104570, + 2899537593795779194, + 36654665375231100984403035153, + 37931867129011624856866447816, + 3299727063330065727, + 60214863406875943179333836029, + 44737480195628835925866564263, + 2612139228647871588, + 19948550379545968435562050667, + 1510510533438183344102973574, + 2877917617831672323, + 7569122205637901708892300535, + 16235096062499810017558304181, + 145098304772860128, + 12570003178929643495557612505, + 28669656231763300538268439053, + 2139975663817664847, + 32131246467572660259260745886, + 6750328415494454812066579075, + 2880982658192384204, + 49788610138472688092432944701, + 23479836874117798242279059004, + 3021633747833515682, + 19741816271246249895018036842, + 76192563287940890770325030022, + 1218074603868831013, + 19969514074043288797656751770, + 29653364734130677996907140648, + 2430638965901048281, + 65323571922941023888004526713, + 50571554272295867492146000922, + 730103831965035255, + 23424636551607245292836380269, + 63728078249194818074190176498, + 841784620169234094, + 40391632975473054563422272741, + 78946187872314379075788154534, + 1318660096698494268, + 22206923742056696815072949005, + 33183907878203904426578455682, + 2455461237432674722, + 65246045470053879344825060089, + 4016004602226017727241515906, + 935519162193376837, + 72477623689639801968673784844, + 48949578012720313350273993167, + 3124768525678009119, + 74523777929667620912674615365, + 7387673023488296043859513362, + 1121958210597254100, + 42865617192122271131008659627, + 27831901356957438254127664336, + 2971725117900822161, + 2489535932093879483932314185, + 21450546949794712782752490372, + 1081151135585680731, + 66528722005991029139671279532, + 79085833660538318724813327625, + 2799981456483951093, + 42513495326388924239533893012, + 9479437870744978043459972251, + 2572746447919483437, + 55615633160457255609300527641, + 17316603048710357487668183264, + 2904545493065788814, + 67670616701471814651548671525, + 69944705520181922001027525006, + 1566365358979450392, + 53461539614964377180700618348, + 47847168174681264759025645887, + 2510479229653405142, + 58041560830504894072090618081, + 35573381830842434929876069487, + 394128068871137210, + 42969426444429635458517366606, + 34735758400409625604229254014, + 110600333579264852, + 32659002650406447375184315235, + 11786930091318160647859219638, + 1939257942151048163, + 65070828451739031855308857919, + 23869024634814421649495582067, + 353176751203034531, + 35267941017189790462940728275, + 45196425096876397500500699140, + 2422003143785076954, + 8079363596395731311642729435, + 48019877078819582881515824463, + 2002105108365767397, + 16704006504648511304488378583, + 52498167450337568840159502251, + 2786830676545632668, + 14580939027869492175683271844, + 17172055272894408364862855570, + 775663141219997121, + 13258829840205900452236621179, + 77810597581934866620827558663, + 455384884086213521, + 29111562001972749330842878232, + 32687562120150032126786838942, + 1576688327888444744, + 59252046878038588704259888803, + 18885361257956546971350131345, + 389635675390060172, + 60121050409907471111656306090, + 4927935665119684686864010253, + 1903748751617781134, + 1301683907134408777580363463, + 30828758691926454502302249855, + 1872982092500301746, + 386796682586193544803175247, + 26117496664220752209791917971, + 1487185677901711898, + 78391771888070948004787219098, + 20494221315342438378392435250, + 592693641208913927, + 73597649048538265073947874310, + 6717337750200602407864289383, + 1460870484803936478, + 30765948097018792406699967480, + 369386580739053210649522607, + 1332643806570666372, + 69261162279326497058209307422, + 5028705885204892075499876892, + 1070466106334529513, + 10485417749990479349244478359, + 45655657945926627804759589354, + 1739660548121856586, + 23449541465405397803179889804, + 59850618030636644683143488715, + 372246468532029416, + 7503464132384986843888881235, + 36759931750097001004695386829, + 388538845399265385, + 48153280610644800968935262228, + 29129415461130264837618930939, + 874841949781344419, + 69120297807881114933156680442, + 31670209281826955844909699630, + 1172554906791347755, + 49377876798201302529932712653, + 70374958232853983982942607366, + 516400756288444576, + 75385278170689822399310642257, + 2243609467646479908174243393, + 161640330202485849, + 58568624185865308025477046184, + 13731464564684043935561728816, + 1230737790801195416, + 76337105189836836556739112743, + 42671766730601920368369955244, + 39855617459200185, + 57887658164617416520894949184, + 31589469809355618661391943694, + 928803999425406180, + 62723229390041873451961400169, + 58553817032000330461420657246, + 692621610971899547, + 71212042220557538361384291557, + 70332729782118540337048637489, + 1941461583950699696, + 12262395825983301790361848765, + 30264622477326253985775604462, + 1754440679508164016, + 62927215578073043526804357671, + 63145762430079284774863666834, + 782180926229193305, + 58015759002867680750269133560, + 8211285285083414992614201161, + 1011329531563267015, + 76427799005964787609887225017, + 23254494387300360853420947398, + 110218216880943838, + 3395729747782447052273787256, + 22469181693941561489193773318, + 3245413284120823845, + 39969135061533606938598424207, + 59604464031524351313610231710, + 2674829936350100679, + 74823520908786384445029135950, + 63214554877538057106380553693, + 3120907164040133964, + 3854841843457521532518120077, + 53395861588386170878316086673, + 456877643106660113, + 10087529383586552616092882372, + 49983640630474338004812860307, + 53504049294303906, + 36700615320368157541145473367, + 52272840185971720139818095332, + 2270857168579560649, + 44291308416766102131592803406, + 46596855989411428689513743083, + 641180998821467042, + 51402323036384918150873057858, + 6507305304880173572694211949, + 1013529209399307048, + 6505337824197814766697181091, + 14817188186127371026907607396, + 2290520174613661474, + 25676920359808181282118125203, + 32942070000147854867323801129, + 1924119985495148459, + 16074399881442381490494857078, + 51769464049399448561028806069, + 609709541032595589, + 10658198317964838605075606343, + 29652139561724380963293586110, + 3466392388193751437, + 46079739405061416302328925052, + 25330320921240247393620171678, + 2026295524236134222, + 26824696815404456825691286585, + 20436196581877463101912553765, + 1344314219525744425, + 41792414699506346088923627176, + 70436405168900772777938059040, + 3194661765159768627, + 40796254101884337291337664902, + 44880275457518672430009559493, + 3359266830396728899, + 33938099612460147534386196139, + 63317143133209950391193151218, + 3440183594496897811, + 39247774487643473294802219166, + 33620059930731142041452144111, + 3268498883236043785, + 69560546643614395868442828532, + 57104329442915585052706840616, + 3134018589042232044, + 58922730738928701242807144774, + 55593643407129199123964840945, + 2396639549070671583, + 5825303327224805833135064672, + 64318820003877140801186223457, + 925899939807975754, + 41950708217941798971984119367, + 7902818895087037858010110913, + 529651062490002776, + 61745895289557507841567725881, + 12500977041517458649634711129, + 2572759604966513696, + 53991170855294676246502152981, + 47869285703239080751564426760, + 3408557250295103241, + 76183786505553045116466547100, + 62311536384056319526263919129, + 2505588741249302417, + 55378321920109727822877587426, + 33754956111667645884298999406, + 2650224678766975291, + 12727378359467563401394294143, + 36980254123380517776393601694, + 725038117105724905, + 40557552176992090895534450354, + 58100168644737843064513158658, + 309027548001866820, + 48820218235106142320812338964, + 72857899279204159672969725404, + 21668538560353349, + 44158079773269620103444264986, + 14006875445602651345857670632, + 499299502849587543, + 10770704348709051198333398299, + 21834250024350506623293654203, + 2575898254250535357, + 75397217968462266162488596667, + 9671793046234797446499974963, + 712667541280663526, + 19149751304194035284300424039, + 33302273322942476922922815434, + 2749160062644676747, + 34935141933120409261290273039, + 12867882868434450584686007673, + 2519775121416736723, + 29415993172961801784302250805, + 38447305657919021868433808363, + 360189589821793322, + 7705719803499353772408531157, + 29670530233998258800943679369, + 1965505428490391780, + 61549620610057011372557063514, + 55012816236907955271072642735, + 3414704992599409146, + 3012509530227466384484376263, + 58700303232216584880493909351, + 596323226467269801, + 23169625234310258084447954845, + 39767373605372833790675832480, + 2127797445231991251, + 6680767321482569022213150566, + 3744439488757188185133950809, + 3232803668761320735, + 56695688994406671165824268867, + 6448178029980649223806962900, + 549260655904843740, + 31345504073824316340361180646, + 19270719838821844563242236017, + 699368584783162430, + 28893918806851694458069393210, + 27223530216054249237875097669, + 2426978253867445725, + 48082797025250164307369663853, + 50815570072320394216359201272, + 3375254380719578772, + 65880857104563864358990724830, + 4157594540533580124376434454, + 2616922805299635917, + 56495297903364529444673901372, + 40423054017571479717964893058, + 1894529235595288183, + 3798754385093655218317024318, + 20803697680967678955804388755, + 688877935948193929, + 27157171314346011819950439555, + 62683159111872964406178902921, + 3111434790775397287, + 270366114425720967074986168, + 72920443426333139663769536463, + 3240787823660633089, + 31262103938403000417545024822, + 24662723990735394073983190245, + 178466394413101691, + 11901740614194101799257566406, + 18680294995349477833634894808, + 1623576848505042153, + 25911732717715624911376186924, + 46782636701134337102921789960, + 2542247102658501413, + 28421130131578861801183588790, + 48226262496571674645268165485, + 2794376027637053865, + 25625522169316096441543204622, + 27415300087174243311297656295, + 2574209845404891536, + 1437387124471198702543531175, + 62295768205003516234051004134, + 3458747428401961082, + 61385258429541765664743875958, + 32215599626365111745336427630, + 1677298869860817487, + 32928276767904848688838760957, + 61031315343121822468210880620, + 3436704126128700174, + 56101500821626700433439789143, + 53909124381979047864199482530, + 2048548455024274996, + 47533650160915184746218211413, + 73004332672020395884918527946, + 2892343885974753985, + 71665086111594189335563266432, + 3493044051994348389453015655, + 1954991751816354105, + 19918599536989481104649802598, + 26122210417229417323556393531, + 1165611544872871105, + 52073635916667558592891515739, + 16919135287822679683609276015, + 2337080258500873966, + 35046291823497111180710966112, + 46891494151281733924533017134, + 1721477613133183059, + 44107241797335154242327960800, + 37652678407600631069979872083, + 2932592729659099566, + 18314993403321574181093261217, + 922068479606242376340905980, + 582761004070635338, + 25151916841667801815706839503, + 56394876487916194336672896078, + 2054542298919768343, + 41872459553593469520168234684, + 9203859261626213241259386740, + 355146184217215530, + 65537043204922446186708989479, + 27897648391184281963247640155, + 2659083712309651567, + 54617914243710832611506286520, + 13186755651217225308381040423, + 3023933572130339495, + 4009833300076161090486644104, + 36970095574116649056417704561, + 3285466408767508943, + 29232220240282320887064654380, + 70229892693282272845877620696, + 93749100336423302, + 34451588041741699521578215074, + 62191675389754551286358760428, + 2121887328371730541, + 5430133548922163971794121870, + 38685271032737362472943096892, + 1136573918443529627, + 77346515234951653618693955874, + 2939288593441536059022440764, + 612121182297266685, + 74545021846314487082872849673, + 8239840421895664737412378354, + 1327527828678071645, + 62386658665161144630500913805, + 14591815809959424787042300769, + 3363150284111074732, + 43131968026419018285899945204, + 43495436382355374144681079523, + 2807844329618823120, + 62006640498878731510967416634, + 57129909430091174065416798882, + 2773285722248013885, + 50668036145982358697957314376, + 59917126698276282163524063773, + 2568297028423480056, + 63901009270320943610633667512, + 60291706529752671371947639424, + 274597011884648224, + 9118696598812956468332119686, + 69737215679507637891215516836, + 15085549738218108, + 70102165893358137880044080401, + 8420424105814328963857761781, + 1103517741245004142, + 30787510874855796557057631090, + 64534282913290447720284100312, + 646419888314065258, + 78078694699720769199268115568, + 23209955970842373556875994076, + 1210824625972115568, + 49996320451706071341114285889, + 33104894282826835367019387721, + 1898186745713622136, + 39608755772225650459115713790, + 13593269694747435078130977450, + 2905388570807707651, + 67544101308691349840120391049, + 30286308910564312398691288597, + 1188603996139591365, + 65600538410022223834253540529, + 28055619808999914116810530992, + 208928981395422904, + 36607820253947548827515650055, + 16721459922646909914912309152, + 2593325900041546366, + 45308100107550093539226445512, + 75866381776001397937916484037, + 2631053735227816497, + 72906014925774500320514967340, + 34931561410602188469987059069, + 1433477024031929429, + 58268146305390146481219022001, + 59124834154191412865558873227, + 178528398034183274, + 36771438507220481384297320756, + 24402102853735646136302741395, + 161144968344955628, + 65236143008991878953377408296, + 29418041656156567196414916244, + 3206680374806169388, + 16887764303776451527027813734, + 49810941282043064021256398524, + 2044321236359671825, + 496808051544518293304687350, + 47939047353745347511173580971, + 1152121385777855634, + 30825745429403432385109334717, + 23009354538328525879241762504, + 1007118270529912583, + 76312776849154121931744513555, + 11155750137514297643491309729, + 596194297242498706, + 19870930303293685463446637502, + 71344433487827888695714243042, + 3143986047894049507, + 65606676213492446622191680813, + 20027222454541699255877517905, + 2948859401257335768, + 54637937968329008719480899698, + 60460713215658577778631696075, + 2355328846232689720, + 40510429949442381569794339422, + 32257568745048570836549365094, + 196949713481544465, + 45248463849899155519956772970, + 70640453855967151446637037394, + 1797835883372175933, + 35353064637808043204631440165, + 20975697205910912885581149870, + 1175197173385268503, + 71514981505964951823832453572, + 10462723760160253661814999757, + 169949080637690396, + 25671265100129390883022857812, + 49770637146820657572738618842, + 150538094841292512, + 44779009781337116418558601979, + 78068179635337706082554621688, + 2877406797548565778, + 15457828696230967567654531543, + 47700607091827037593251901681, + 1755476504850160034, + 72300840649267623300892907377, + 49220057503696452836735393424, + 895985303971693554, + 29400122870967316405084445267, + 25217811955120412325018942907, + 1525969358832619890, + 25126877434829489386173117728, + 55773535837266283584004184413, + 2148312005768328749, + 51762388006864031261686547464, + 33534854258491210439555333586, + 1162648829423886923, + 21661135869304175586559515475, + 55696889012785091646668219867, + 3048014999005212760, + 12403410891284264992881967700, + 6578682058362059807310818599, + 400110569839673534, + 65531353929590774014424008661, + 61270342941275274619981192064, + 1853823437657014621, + 36791279069541809169131119589, + 41822486146423119569712247009, + 2523611992856784238, + 12025615065393131995925321786, + 39854311353015819169957456904, + 2066321447518242897, + 72369581639197303052659553063, + 6720005250366655122854185804, + 1098394614646336745, + 33944657431072032194864113367, + 62101014768523652701961497044, + 252965396768026741, + 54271155426567788098665956923, + 5449943067179125018039925586, + 1019342426063192013, + 75573454130572830193180747819, + 32485575240366941342475179616, + 2217446648994576896, + 64201883776080534739653204132, + 7570162198623990496287540355, + 899004422700853754, + 4933647292097576015128071551, + 1877759230435944377107683798, + 1479302257504575339, + 51663401219337577388681957644, + 35878364690424614850652858077, + 3172087054882922887, + 38723950520677034011587150986, + 24000780804222387944212346837, + 3463147357774305728, + 10346555788678760077313883258, + 58633274536752751186083682315, + 3035596440061903223, + 4503840616447668024451464734, + 36032804934701094790986576143, + 1340832292623772491, + 28378801941401175904712652128, + 36464380360624509436172320719, + 1570654529140764774, + 48607243905050825271011452533, + 52610808353755469448298892681, + 1418167304902712123, + 18291131822771728660726503477, + 41620548010877130494214000971, + 1093883009988344743, + 58739838572473751502408137976, + 36156660364151457888356005102, + 557328509400801203, + 78773771881538207714244038321, + 57720406695719489162015064470, + 240079173335378692, + 30766637435876570487079177512, + 68448237561257162008037552099, + 2125306631335798077, + 42225664063950040558753709748, + 61627101873858328225510628558, + 249606303175326981, + 18586304255954309813774130398, + 10232324537759938128303296616, + 648836934447893907, + 55432939885278504918286813543, + 34565507447992020305173156144, + 2009357110925638322, + 37839054656081928627640802586, + 49975440016041954394333295636, + 3321115511729179999, + 2920847204431476792058987781, + 36723905541210099971142289805, + 745003637483362259, + 18545752581861187933583029857, + 65359774693855712249826239104, + 2047663912573723579, + 50863157247549961322994938594, + 78153686576471230615768887073, + 3020196739030109323, + 7078079465425793548949370683, + 59047862914917821884697428600, + 1843054053793837795, + 23436036546519500693562451923, + 49599096290291206670641054589, + 2663332504740258228, + 6322412754622806235447913389, + 29467942353784666036188203046, + 1168469535054751163, + 51356831467472610342859596578, + 20753409990097176623442490388, + 3317562758812808863, + 70897372937117205649162646881, + 23392846564325767148572916622, + 339735363484759366, + 9258047694406033508983532700, + 78693045851837122150735911198, + 57152084243313305, + 66454749730896290897586305899, + 4443515858366795091682667404, + 2834241704927038961, + 11338888435047093201894721009, + 37942070255772407449505822151, + 1274042872247314863, + 66090712021245060578619758597, + 47396649989794804533788643715, + 229078585991479883, + 16567151091358229096874401213, + 73904722442615189034250804604, + 2306349856638573361, + 18064484836559854573144814593, + 37470647397476716642733667720, + 2506203968314921876, + 23942918953675355122177201832, + 30388973198103866929416581014, + 3090423208564520818, + 24896695193871678177386245110, + 11916453480670623081367270310, + 2909785205455508246, + 34477334444443878760751974964, + 47861532307986362445469335014, + 2179845893979875032, + 29598326548212373633562668390, + 6521455930768341291442125035, + 2483638381044972846, + 7738052702330240921334254556, + 31759966751182752809248199490, + 2056278887185431542, + 66820410327170735750391465125, + 25509235906549455209358083664, + 3334942964560378603, + 11860750579921096031697231565, + 44309369941141807370126102628, + 1577017493916263922, + 45331538478528239561372691606, + 77186177518049777909206261496, + 1677382242827578588, + 58894949108008018011988414461, + 40231823643678313431315004571, + 478887158088468590, + 53432420485060728009438011801, + 75388756460260313940191352296, + 1830118272611079719, + 48412693358667811567197350591, + 64083195399018429572472341140, + 2184295458803233439, + 13585862039618211954060269238, + 51670238091331909902210958072, + 585361744155266167, + 49843350609089704660194181593, + 41644700486473167846404073421, + 1127821335132681800, + 70687534494536680395779927031, + 47832263553841350303194866008, + 2945947678121763326, + 31134043290352175346930913933, + 59251031560781834074951444172, + 1479801893283535893, + 5629653152067986455099959502, + 28361289336683227565281024992, + 348723445560118855, + 41174360818906785605066051049, + 6530059292332177609887514069, + 3326893614889673371, + 47499920533974715382891550988, + 59566331742095449749992515391, + 3166814124671877632, + 40406166108429604115259887791, + 27349298349604313170697230317, + 1074590363098045568, + 34754671901805320422914445894, + 62552521674136423966377262200, + 861045070209234811, + 37615017432957313260084051300, + 18899532036163680754292308631, + 515552503683544243, + 6195814294416893552391943386, + 28200526831472464732632234005, + 1494846332602330419, + 9947945963657554184688712768, + 25850407644099348690495851931, + 2339254221114838582, + 10565857130169397066972247247, + 51571755546512382647065484406, + 2759309979675748876, + 36905756399724673215590497582, + 35765820650697672275161507628, + 426598912627316263, + 31555733996209272103786758416, + 17649658839324739792060402327, + 1245406037698698461, + 69674317070506583508361286007, + 31097525166073796111532198609, + 1608301427725992217, + 20858990416068256699037563035, + 62493687753542917530470208613, + 1406230502058183681, + 34952499160283741236598792170, + 16042012463981313730777322801, + 360398952371806153, + 51057866503693150892751934207, + 55539583370713019256484767535, + 3481685206174867655, + 30227689685397641617456485459, + 76042500305605976214325458793, + 2627906760219398481, + 10123276647258930065105306606, + 30184578415238461422443370698, + 3183900812899912768, + 32339163543445564568359694637, + 10430285468377976533315833112, + 1880910515681464316, + 41300000370373683045236684172, + 15806060191154700087032765958, + 3170433027212356208, + 35591162090387791251691159628, + 75998442987895464120146814176, + 3358669111606088398, + 25724760894453180897324203319, + 25738766713863161824018625113, + 1107567222335803057, + 35412443082741269581444894193, + 10645905176519130626286919207, + 2419900931420767062, + 70574251389612752531708624570, + 43390322125651666157286350659, + 488564664190316164, + 47989670102596525193717217120, + 38775739459541057904719114974, + 2783274622478696923, + 1082414691227539211187808060, + 20247424382370576275662033155, + 704096607053541251, + 70037678101823043896328472825, + 41043826256713905204252084633, + 810889590457563344, + 9238040911030902463266809205, + 32337961480273547282043937492, + 3289856637314987835, + 5506974736864191827965145344, + 54876397770030840948986617005, + 3219130764604087973, + 43288081614711371884954084857, + 6322088263487978832884324896, + 2375862582213999093, + 50431198492714110062829863772, + 8999274111593379097562441578, + 2003708471793823478, + 15686688504243802567542102084, + 27475496725629396192308562479, + 1668073840923192161, + 50735189927231574648491537982, + 39010253483494922519891121719, + 124994831767919481, + 24103917101724663366569806362, + 10582613180532823602807480400, + 2697168555395050806, + 15154888387511152997044233584, + 17588159325086739947746316990, + 416643556909981173, + 66992633597860178388799325970, + 59638915566942292726650400872, + 197917291824799808, + 54561563644348742036395153412, + 55309548721012238453676944253, + 2442695275494439222, + 27674322342939834446255820708, + 77564663402645166969557575830, + 2028685079762464675, + 50091708221410093474316135701, + 22178213319815789173184644615, + 785184939908865796, + 19277097941851589938959807052, + 78975042696996077776787248446, + 690998681758518762, + 27634483377854474392663680954, + 73828821641273878004803137391, + 2238234344438921793, + 42111955970471062346597520818, + 56298792007212632286103357592, + 784896657539880562, + 33769407526467354798272764540, + 51055396434869328090673365851, + 1833491225553326955, + 49550935951430446107132764105, + 18743899448331580426309004613, + 1782287575135255810, + 44098267027384166037197347576, + 69540799507533527658509858108, + 524148807016807315, + 34339363776300993911772543220, + 56008659790014908191103526079, + 2222858399647490243, + 44431307582730936975257213617, + 65665875583852313687822279172, + 1892675375076580801, + 52908301968234931346913397515, + 71134359284109481998743460790, + 1960689462532315482, + 55841755957234953760055026670, + 59625109117557371166006210374, + 1488125298880781374, + 2174986513657856190720221585, + 14488538198198991286159793610, + 3109029993791817553, + 19624822096045534211670617207, + 66819437423948683370012715199, + 2079118347791784851, + 22071091705295668309102055909, + 68643982520829626220621350723, + 246786528973124749, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 145, + 24509817451301540223919621757, + 77644142265560849584615403967, + 1460461595816737407, + 77998445290821004855181052629, + 8508447448054967720828016623, + 1508943973772180707, + 61168454092740294608138206155, + 55429116681057288347990141026, + 1846439136030873237, + 48936370511603734099001532387, + 1886399306109197960212530135, + 2425468423121125429, + 14421489268465942664472761247, + 77557038598760992442762234214, + 2158405420881108538, + 6174852459311374880878040139, + 9620273883230540057253877816, + 212838158949621424, + 76383924675498500057016865296, + 49267497246548390910703327810, + 345240592704614719, + 6291235064852327818889941366, + 28048782671644824398676109618, + 1509213479409512517, + 55174635069634869734988449812, + 30410557958357577813290201085, + 546200197723978965, + 23822290974219130903511295374, + 6696145575670515713840370923, + 1503159267226237490, + 6749725686655480236181543108, + 44658439060052632176806763058, + 2428624667501014486, + 42733871451301890920078133676, + 72990398683010378757311098157, + 2609905617920784831, + 63218586173945602799868146066, + 18345886111129243715598684027, + 2973999382264831436, + 43653390181411970640437212327, + 59499746923985968542820709991, + 1160912091456134837, + 56744177747014809873098744372, + 19352431110394735358218916281, + 637357367865845112, + 49288891051964484941055577837, + 39344554348756233703810459749, + 1724128456671809146, + 15324267190689542000105667978, + 70337935446199040120124257943, + 2423915465622889768, + 1909869517735668912561680513, + 58815730107348976566717259663, + 747895842853842854, + 7421134550162069172148171939, + 29441612744021276570377726502, + 856080883052915169, + 13098898640947100914113734821, + 13358713757529460637670852240, + 1988081883481489250, + 33320063937947843167055574417, + 9512256090335552568626008622, + 972965105891366128, + 21047599814038789989054318644, + 40237891858536229817282965766, + 167261813739721424, + 17326264106467483746292009195, + 5705254367162197162393743271, + 2642129168415417598, + 70576310470453637104677015937, + 73074781924995301378964657460, + 2490299470728738447, + 21348188048814585735820040456, + 20681496697788261729135812184, + 2689664497141853755, + 7995277239419954050807235972, + 18512984093852110793906942159, + 1660946254102537912, + 45646435121648256421589449095, + 61426330608918812832647024438, + 2222872676787388450, + 57154535064158217442133756992, + 8155726425865343730469996972, + 2934600735248658077, + 52116894783077303601661767472, + 29919215074923490809622215625, + 3029300018878760178, + 53431984715598832187006373180, + 43306668828693360246770059250, + 1859225476484187313, + 18963400823938796912155620032, + 74938991302106453275463556468, + 2400445172407521484, + 54615501369260131370640113929, + 18175245132009355030173695389, + 846231644281969816, + 36941711215381316760005642769, + 39930095438573480704144070123, + 997499166319838714, + 77007175823942280373360884120, + 68217569534062364641587775057, + 2292499274375538490, + 54011078273788698001026154549, + 32925466745880007893003423609, + 3187929912014411043, + 69484056720750566466736752505, + 48505447366136219182157997255, + 3113755209251086715, + 5716877686681840064273270950, + 3542450619029851951205933369, + 2527325131535147881, + 33026517403450064478716321806, + 68968949428899930483587749130, + 2765458495253813645, + 13106094733301918470565115476, + 57253393953307911939527627069, + 614979351215330388, + 28985424926166365454374802152, + 41823446354681944578573976623, + 2216700027658665326, + 66916569935834222170834695983, + 8910762612655205919119729078, + 2696381194380167546, + 54444971666178582646827415257, + 2594091069699355791405548275, + 1749731028281760040, + 29720591913822461161183615561, + 38631222717485351694149496070, + 3375906834635938380, + 54707585230073103944630672912, + 65950864895751608221124094253, + 1950961546269112208, + 20145112894571866200865553819, + 23271268272902272027539140152, + 354553114954283432, + 21818243441811918946684025492, + 78847344194952675197329354326, + 559626584650429815, + 48082260862695090202803632212, + 71839926829377988142024009540, + 1090717745498356238, + 20357447690038204543314606286, + 35828027882800628588080154500, + 1834414984500514730, + 62470971090976629428013513177, + 58987461853917945705749892749, + 2363157254597848263, + 13168869186368284016529450372, + 22933350005300576102983996883, + 324758793080967108, + 47324197362034054971314184244, + 8709173076404260356708166090, + 1213789095456316981, + 65569577837524727138843093206, + 68007880237935201853908624540, + 319390111473971214, + 19050958744367676473163095400, + 43659231423131619955550602183, + 135302936184695710, + 17583872963377588930452087608, + 15958092616825754714745957873, + 3005573230784168281, + 65150814222286042814535087654, + 29804438609648141745938037112, + 1598184416538054720, + 41404350795116168889944844387, + 26642736986220559565054788408, + 1005234152317386420, + 72383660843444573287550890981, + 22244355419056265651448521269, + 3094388145904219310, + 29063759737621917925066228981, + 42366268108162758353869713449, + 118208508025517915, + 76087732038253528164906983891, + 17062764179133582715047414893, + 1256127643699482925, + 73014604419299088043962393490, + 50498591533874210793613933336, + 2535737692890958379, + 32532168083074116302312238198, + 53941283403227511141606356600, + 294207881225859703, + 38106919963750962605709642378, + 28153202454107017353032420457, + 1948168339253163955, + 11073558291705955848282973580, + 2755379922301280909839608771, + 3337351174128284522, + 2325203385438771489149146241, + 61544557107492270750019005985, + 209243626975770440, + 30118518076112779637912958205, + 2275656866247055862990065547, + 1269627364476078629, + 64648830510393445899191151528, + 70260694043333853995258336069, + 2381318499933173379, + 17898220952449604810813308305, + 60021501232141489903411164306, + 1928531686982715217, + 30115504265620639249007052898, + 13980460000940023655605914225, + 994979394270689869, + 20623768875858226571815064145, + 6217272242486046409870063143, + 509590544726450995, + 21979825864494594491476156580, + 77253194633739232083665788169, + 3359050698067184877, + 76260372055606725413238096460, + 47474697279095518055214946113, + 766550924367039622, + 47395797774091929015459792144, + 63564889477437778223135533583, + 3001523604902281037, + 32010202626624669175341846554, + 14637800153365436380027467201, + 2526854982409798287, + 6183831856987075962846257458, + 99652182929493063453877464, + 1206203499771960332, + 53611230980585678104146238346, + 29257098188420991891224827528, + 2226012411920814573, + 19737280489461087937922449834, + 16239328560788421662613198114, + 1776474543537219983, + 59688186389072628259136242084, + 25627365847892206820867400589, + 300076100713497908, + 48619981692664047077107995681, + 62386164378391096854746394815, + 677965174430426838, + 48036517540052799057662726724, + 40184395274685342068252600252, + 684571019642757554, + 46798717494746794345744719906, + 43124519484059579440042928251, + 3274591302033327237, + 24265109012172962683177304205, + 40622642722995618195264696270, + 673262833569562202, + 24876873025267999904159966470, + 46316917849195470602560961400, + 127441211661917079, + 75578305798461669187396600871, + 39895643943795011267125822276, + 195984174603737389, + 14282776036565542169606410025, + 68546906842719096064233153935, + 1362034028901260503, + 77814140858852975761740900509, + 59963325349218235080303283923, + 1086233958283594199, + 1574548626816988624990140601, + 52564443658347153617080858770, + 383600446992213062, + 51933862833015508809095032573, + 14839975216697359227782833025, + 2505650461536588028, + 36959271981559607476875387290, + 35887303149860007025532387489, + 2583600952178461978, + 78113326963571014551100657270, + 18613677198636135655706654359, + 3403388542597532131, + 33180830928153074573659688232, + 59121368144392388762351124817, + 3186007797681476033, + 26346757770256549959361384118, + 17729862079697924434179830972, + 2591028152960443827, + 18857516521104876390981767942, + 69371745022739383228219704767, + 1198035879415189209, + 74720295839793580601733017324, + 34321140791015394710133343199, + 615665437553029781, + 69317220527893827644880796057, + 9575078927682660565675292647, + 1893767736433358795, + 73571922320770489110499890492, + 70608105683445128788812225876, + 1865560555889765063, + 1930408203372600132060542269, + 50307239549271113051502133705, + 213488544905241338, + 46449572574993017135759810878, + 1654611241599563209650766374, + 1209189281936654452, + 20061352773856452706186658087, + 49049767257319075329615079949, + 2831198826923516219, + 75447568391811682892705576926, + 5280560434128650509067875070, + 1331602459487815711, + 10044602740765766976433252934, + 13421578738208219680563745481, + 1161196128848813035, + 1429517983472786995984907116, + 2169164925972794574577746030, + 1275396168395123792, + 40176938748680849686092885029, + 56122802741341405527579699563, + 991185514172561078, + 38651729958160962254624381037, + 55139478986807644160099824699, + 2096297651609718633, + 7785318094479033090497869756, + 41806537362443886415149679549, + 3352906472452752363, + 32026193177590271013880533860, + 19253724330633539110777390144, + 2802353952485950327, + 49815808235741047704378955068, + 30382804135230307265369604977, + 1455596001638233449, + 42814007113695312365089780040, + 69982012163144171515874154692, + 3222044727449480845, + 4650384156295165949456576212, + 64236476268658646732860327942, + 2551839434870733190, + 41617952586368525252419511409, + 56058011638814267190414680796, + 2468020314528106974, + 9332762588452062155588036784, + 70258732473173154454559360439, + 777659521168085634, + 4005561444031824891344329358, + 54562447062272192396900041068, + 1397245195271542691, + 76314135162602400203913902047, + 25134792182995986767365671608, + 1174873829208000366, + 39372581244270551455300935134, + 78539202361820332694401501242, + 3400114314237371886, + 10227083118752655186017808463, + 9659477110958805324056963231, + 3291791965680655481, + 52579058164986895478210971782, + 59873347169939834766424109098, + 2939648477815910146, + 56595953890665740613982185011, + 69321994561228245191114317430, + 1656165609752495215, + 21525782962555846968622644387, + 56881218599951836860782304485, + 484010061797868124, + 23655683925066995579148131090, + 32013999475476453665858683733, + 954843879050765578, + 43749806855472807078969694918, + 50183760775680486307437181963, + 3117431620874066518, + 68485517540960121419465249580, + 30496635717757177397256656461, + 2725293666203869495, + 77265310065763284725875976355, + 11655538063140438357505968213, + 318540598211364039, + 49088892682417750805675426312, + 65613065820674989849441321209, + 3412785532402890852, + 10098748444375677231321526640, + 29052324118809805810017148955, + 3467673277337242364, + 78562345875877543216464971527, + 46617986047402120312168397702, + 2330384665110217170, + 13469910500556730868514655918, + 35720324474079837204466855078, + 393143347486975140, + 2216461346603541354867403240, + 15570720706544582326038130751, + 876206994889648779, + 18050473945270661442804996333, + 47947304685978800729764803475, + 3450949494115882292, + 34224980333498518083283176113, + 24857634294467991375130694856, + 2387341145594959587, + 41775178214349813862635041923, + 64279676575006196506881908423, + 1830010157841959531, + 12446290167881062015175484970, + 61964291898109417462632360035, + 1406211795853042998, + 21204129857723232957641005836, + 574272106133708130077630321, + 1218742271039688406, + 31149646425108051843924435913, + 29397399884422198644015200979, + 2387188228865636937, + 12258710630961060404645969533, + 8379173119266099324173759160, + 1929879413419789060, + 43980369158923236149798561430, + 17533456360722084825513315312, + 2646136461522837458, + 49070126092560209240225536843, + 69750202925444314467593742029, + 3284726920573749525, + 20229710309345521575834298883, + 55472327923894828006691362906, + 2207413224199779991, + 30101672404045438476586849532, + 53010271598131845755656373432, + 1181121920408104274, + 61060325030509433661765291731, + 19889937000733411806354218390, + 2843070890070306689, + 77115475869387958251992178975, + 59552303016470484224522035020, + 1305937220937845589, + 40419413424217210460866962567, + 25667773903796324402598860639, + 3240278122098800181, + 57999328287868511326952633870, + 38762133967691609951300508153, + 3167297754804850413, + 5449514538268457833427489454, + 55031709743448981648657405333, + 2199189794299311157, + 57672835447397080634155415855, + 26550635212230808317610234024, + 1386754133229530488, + 62825574339748301615985801815, + 43466086649653998841904867493, + 2211701840204205412, + 34169841872661052832218694824, + 3063846047925203043367396133, + 3072896035421769475, +] + + +ULTRA_STARKNET_CALLDATA = [ + 2808, + 32, + 1, + 1, + 1, + 2, + 0, + 93492569705772761183096382943568120526, + 48381375842447371862176894966487046537, + 108892489790625465941038134709497921858, + 31553121233089882260783539337434819177, + 313788157447883438835058217963236691121, + 19570789212101600643721604454518588879, + 63121438458343251469824282752325944217, + 46640180724269545065992330023439179969, + 128137402165770828503488690514142526808, + 17577547092075648337766919109542225915, + 31402854409629339723711987293584364752, + 13553788703392864169478279281666965726, + 7328163057324986380960685547530807675, + 16818951948308270205702238282801511857, + 288813996749633455382427345769354348535, + 51502775126011571980254586516146643923, + 191388614461976699118912261481307978442, + 27996510122218954915702167843507575053, + 228936948661082087747007694576239613429, + 49507468968547127188847863982776319699, + 209278659436250364944289323786947160431, + 18442303040687767272347204109349832973, + 183426148187459046204931942631981048151, + 21204702220791217408580219923430013058, + 209278659436250364944289323786947160431, + 18442303040687767272347204109349832973, + 183426148187459046204931942631981048151, + 21204702220791217408580219923430013058, + 327177635983096560824040285413308196508, + 55112388812003869296884895526250267084, + 115051836891494085452933046062317029371, + 46642611263261934844912849350390086395, + 40, + 141974082470189105526752875258780964881, + 62003341022018739433930590115400015181, + 251746922683058886326126624881658701808, + 2320423591164437607931467369826024207, + 226043320228792311318719797697534130394, + 10020626771901853571025709468528019862, + 129236482086889150239228447180672833091, + 58228367890429724373625972965938619853, + 200084322685224612710093527004681504565, + 7017521215519379234650209740973155238, + 78178293374506447844977648887422058165, + 47410073360464118340708374900229926587, + 159275790466427039444124692482101267181, + 42937405927183325255713572875792033760, + 144221141547574721146781363482672644926, + 20095966853892509406089653700542157937, + 67325117503274972898333303555453605794, + 4751527744008468883852396573614021688, + 302785961754480138947549802592249858335, + 12801550832788317597255633058705494786, + 92275875367481986359531175452820317058, + 5451670871479378072031810579541817228, + 88860154079454423901793409156145361411, + 35386991434429957121146112178741339660, + 335826243635634782576958026440189412406, + 61922491219375175502799662907465667999, + 275670649544274330754205880657155011166, + 31972945504704643044871395960008843648, + 149262058102471413422171550610441916743, + 11928895338612055421852617197500122551, + 15612702125597647471941769121459836545, + 623873346838069428181336691064489417, + 171501802532430362225201749554144748191, + 2064765180996303497423984133158974951, + 24995608256416630751268519570767252205, + 21521458229268505029123007332117995626, + 208613920985402387748563297396970618833, + 12373275799527391588597041540009861078, + 159388906289640914136720832381183408955, + 13786113398993718003027229802025232954, + 158833036444882251135679038677112142832, + 14198014564032548486006026548216748459, + 260893659676064025532321622325789007415, + 26262460766947513252866784891164259134, + 282731152647486258624771661961655121258, + 32518325141299437147741425987094539341, + 101532404504919304203511080913734190425, + 20442988997323647727759783588655736080, + 84318081539397845235308423816946262174, + 34847634446714812361405356971521052808, + 297458708221208153922078931771595248792, + 25203213517933010486250852744383516237, + 115627351986646146258402556603212864399, + 57610216012604340152814315693543936194, + 189444903009215207858655446044989231825, + 25960015638490987836935250660841823903, + 336552439919850125541900021752748608361, + 58934598693290672708756114295060570827, + 330163629640095497696648686863658790102, + 10729431883706753216800151468248826073, + 12057951313758258950598774762913059008, + 64088937525168529796672778528327282606, + 111546529857811484014576622682295058217, + 42990867652708924402933615610644485250, + 151069466230733068046084905949404920821, + 21207409835518521966386046655476271653, + 90127694396900788060105691837876430200, + 51925113481914836013640517147302855154, + 1825171143750984300646507519726879445, + 56397315481282393001149848697460631723, + 88285050835363005291245450150352691531, + 13380048745194879504235879165279447503, + 60304485106266086245810339255990067973, + 39331476986438275278692084054268245970, + 279859375287371343104469773592744909443, + 25144661010481795389303826157012950944, + 146497761763678987724236829344241296043, + 12975172374170911981758734351099255962, + 186782449472215498680273575506381819698, + 36458840747368009055845201065860043818, + 44, + 300734949709650438525152111487038473810, + 24022692036705914801628880648254767478, + 52791705435977323407813206106045225574, + 38349083675589734785825865294681817162, + 230854144514338250086343323830680207533, + 11123164629662296211269308456233516976, + 289116535112386363954430018711571306009, + 30898676405778189690831037650819673848, + 238305987423749892177465160132842731483, + 44798612831163327700667411898385119761, + 226744131714188559679429748866667945354, + 11256926945653633650776083270983871324, + 179808848819746517909986452534018589805, + 15977235276783628741403295850852725084, + 159026277082694591785144246861780863370, + 15483519746168229713407565630803962810, + 100278855043992492913287723383695328176, + 54781390403053399986293398216706735887, + 143273251624947374454622750070602362464, + 56063749738250595735101820353937033371, + 118035546230454889182842560172677137141, + 36078012994750919871111511173315600298, + 62592726248842420002364678856174674194, + 61272447662910225260439045059187452323, + 334475151762769646152185823275014338774, + 55789866521525677930940434452154854120, + 33179640941252286008949372880191400604, + 15386397608093321691867321519389546737, + 286705655184193181628489612499035804723, + 60889028443219618283428122702806792431, + 227390252616373003366133632390652991043, + 53580633767333846649599949875140182372, + 328221892268940208294172811169080784621, + 690373474812248751659149518762721639, + 290356823524097145171658809569624985658, + 10876937281268919980092473719576703094, + 282571398159854645331956457140318669941, + 45551101413326481545629786003956769132, + 218577380399702918850663927091994024331, + 7330389493674299240959615226169758285, + 47619267493840159908249571442628286732, + 16, + 332377282490322398999509271681215205713, + 4101241200720842381529765108600509287, + 24448017445090388701240626643095937272, + 43859548012082308067427302330030892638, + 216985582784085642408149272387586427134, + 32521524510738898765989573161631367279, + 80437415282244314647168277693911278465, + 42874607567770569379227243540100699007, + 114021961564795832638960771353338438360, + 26250187039919275345548134074854008273, + 291234592454059549372853262307440598568, + 42783940955145182543916116660805968847, + 86150079288007163075945184046531720529, + 46385558381978759436706232085287063644, + 176140834449326328822796554736648256426, + 47990192689354694208989733899706339837, + 54664386521565638253569966903685576877, + 47575658638349834670460290616463714008, + 86146348550198876001079900094623364331, + 4006908999766788994685387820639333235, + 48243049320096704474288575231864274450, + 4265790966573350051960270926979118568, + 56070082474258772793104545149500069142, + 10316538277444207836330467555769470997, + 94494502870594005740452684820213698020, + 46616235798315690039727907196507377926, + 94494502870594005740452684820213698020, + 46616235798315690039727907196507377926, + 221274932426534304609349421392555202178, + 19052082229354947411363310530842685462, + 196276281568042338624178460590763071819, + 17446710271060805001211628829650782389, + 172963537602786903164916964385403548270, + 6836999978048659766659684342531411021, + 273027467086596148568663652762780202261, + 3922961455795894331122917414996139237, + 39807904427881875581217569707062401490, + 62168652434919205440304084007630505262, + 199306791651492319361462335089088061527, + 12092928985371545572062481867118740611, + 70651158553171833448604813852091065200, + 31968461653804779918379260730055251573, + 260105359839124297060963963848744207369, + 14461045617965731060373129924254290549, + 210170625516328305925049254921922306067, + 33849235660424535375429920731932918389, + 4, + 244157929495333816374239540027411671721, + 25355078701796227462842193786612660471, + 33389953608891225985662717244171744229, + 1947878979219590814028806696423693427, + 54762000872045000424361380395484634715, + 58398614974826008770179319056951017540, + 179225218824448582081726419597031131304, + 60110997708368410162302892340857899410, + 76269039324282155092486617940324257300, + 52535216886246953071237502660444091896, + 244041008608729206706422888800891878176, + 63140827073010821056051166167267215954, + 84398145630762948161387179519827661182, + 51899590276057970088138852401279639836, + 280408705425836816227096400724368391867, + 3050880760865688303656434193340317369, + 5, + 301296639284638542700891273140791262370, + 3533554423477072037121699902016671995, + 148184704678270660307973963861292189801, + 37952237541179952078349758354257301850, + 97903254201199843034458253541710283684, + 20282387447120048041933363980656251390, + 88042446113129892051837600687991361356, + 8928924782987472932989971994866812998, + 22571868826426559023763797587687510421, + 42081054577150479438983252856121748612, + 317519284504779887434540740863623688569, + 50664256606437176472336996499553883017, + 307452937857947433863064919076401261564, + 5663829107601995315492031350575558309, + 223099909731432768540201883545671922696, + 63760282856211054246390649870207257216, + 160075048861754335514059520322876550479, + 20582465048565956117102168439170466987, + 9008093124952168957023437289, + 19867754246882204990276213609, + 507612330498320109, + 0, + 33771031995242384697331434416, + 62804486796642734476685582297, + 1111910164569763471, + 0, + 67735779029481311405462123225, + 65250658341311676588918104716, + 1382239618076297822, + 0, + 37502319035990656518969568962, + 57315391324323678606818707407, + 93992710295538746, + 0, + 77783474051843046749416391809, + 53891876761126088172090498544, + 2131889057252164757, + 0, + 23235628660412978190750734003, + 59007266736436168382421609328, + 736330255185932962, + 0, + 45, + 78552950390108297954970498048, + 5852571632454181907318883531, + 2446614319042160870, + 0, + 26334938995619689009146703386, + 69142967255337273964962085918, + 442101805105990570, + 0, + 41953920578292817426435486435, + 54823688924163229425442626695, + 470049621481573597, + 0, + 73630098916597107186552160472, + 25027083243084069175636624466, + 2165016243989748921, + 0, + 60719527418878421297006949111, + 19831535882233547647600884552, + 857220238774428839, + 0, + 53242959580421744402989386201, + 59062626000533774066602380687, + 2688242007171076432, + 0, + 73564788497569780065409966268, + 31262855366925774559812480880, + 2428556009867405184, + 0, + 15158251627012242085477962389, + 18639475738974389334337360152, + 1127317439993333787, + 0, + 23105983615577567123304849378, + 50981973966293858106729152778, + 1180292782402984218, + 0, + 69555542368960423939548197087, + 47085351366590016763848718512, + 1131665866752800442, + 0, + 68079427180210868594991320860, + 10481084371519844692320532658, + 201397575536872215, + 0, + 52275635599737240063101984601, + 9647112993592604794246014328, + 2098551556007682161, + 0, + 44821152884363463945578416390, + 33982978483365425539243370869, + 576791144736588021, + 0, + 37413017813260291164035547360, + 72806153742235547746782019103, + 742452267509371741, + 0, + 4275795389900275832523535014, + 78920239592021471044969620403, + 1409340696501574859, + 0, + 3860427614120435035711375844, + 47764805759986432038699846216, + 3128268005473290252, + 0, + 56084016667788325821460510243, + 27318324793367076497946049758, + 2044578405782745907, + 0, + 27593686668873043687358052864, + 10407410233666985955216233197, + 513040211904438648, + 0, + 29100486502441258967673832109, + 28627242323743772496283452597, + 3084860512460290686, + 0, + 11092829363804279601889245531, + 76701805847945814776532306169, + 1763105793877181890, + 0, + 68523292543480738704136676180, + 39276811135808615325212324548, + 1939122560065848059, + 0, + 29250920434507186553917866814, + 4765478959379432697585743228, + 1577602648199055197, + 0, + 57201753203857243968313603324, + 55324632872863563117542634387, + 2475951227602884658, + 0, + 42189769318757233134630462118, + 52033077363325861829100741894, + 3162366059875196838, + 0, + 21868053812088079958965621873, + 52365699825104684219283978452, + 2950285131300391096, + 0, + 74915049161009167207349178397, + 63924207186930113969903537602, + 2138554464388256932, + 0, + 38807127898611152664218762940, + 76612125987871506296815611256, + 2976644249102615607, + 0, + 1057973255327089070057270552, + 47617486304757066880587797555, + 2218060124369630821, + 0, + 19520487011291190424299532129, + 50749358727320079047577638477, + 3205566723767204016, + 0, + 17618388934719708940742634971, + 31666374925029407166413085752, + 818640459869326700, + 0, + 15476258991247539005079703042, + 56493935118673980791855473506, + 3439211973065917393, + 0, + 51441422217389506201894788504, + 14639618558352552894115271827, + 572464212925310775, + 0, + 53354309300102110807601789132, + 36253309452592613001379574415, + 3141331552813172751, + 0, + 59259477620356763301823931547, + 32850154294511842450638187703, + 1303420615473483606, + 0, + 26551833852519130860791330378, + 31787122499685568061214420139, + 474966144231161437, + 0, + 52352454625428838727766332984, + 49773186840615490284872130074, + 2585027533768334997, + 0, + 30008346887327021521223604602, + 38050354940182632754754273356, + 2437776520410166269, + 0, + 53974060352390858434904206669, + 30143188733029117517884592892, + 408882802362328070, + 0, + 6818311072014316759671020228, + 3640122564324873582790184546, + 713422177599455349, + 0, + 53646065997159686457662938882, + 9473749658656001838590768489, + 3459809245372779222, + 0, + 6803627648037236795224420280, + 11709837889774331560993959938, + 1541732893086170413, + 0, + 387142967949503012498120916, + 57039604196573788304351072621, + 12854158183097136, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 46, + 48722531626299927707432760042, + 14198842423075189411209329012, + 2185767697712401792, + 0, + 74207467346487732320478939088, + 1788071070492216246466724869, + 3302028350507744083, + 0, + 65626095760688656278345945030, + 61314277176267736942491362322, + 1060992816630257652, + 0, + 49144000933302771066132515483, + 36539281646317126531502566165, + 1584388316300751773, + 0, + 51702403964272035571481131888, + 58104299130237111878849387789, + 1641799044913707669, + 0, + 78583338437717755185187348805, + 68516688330467817462394301098, + 510791976852511867, + 0, + 46262762568577545247590093648, + 16437675384132432839751556384, + 1518762422224036895, + 0, + 37294336211759615955673414463, + 56852427782220162366054008460, + 2195828980287428519, + 0, + 4088270282129364979828378225, + 46752433114049905157245164161, + 3392682526301355812, + 0, + 24304168848993238155776668600, + 54184425883603612893730743562, + 3381281883326856275, + 0, + 62967810853525408226685149298, + 56249539155020400037154974929, + 2280495447226640497, + 0, + 50133945060747284438453694487, + 52773825724042866082094794443, + 842885175424604146, + 0, + 51712302570697864078886714896, + 2456678728852805401915881939, + 1248392924583842427, + 0, + 45486093017743963473910198193, + 70677861824204583634146953601, + 2709037524347419700, + 0, + 42548615721257383871611897936, + 40233109372895803529399966943, + 448602934094986207, + 0, + 1674600637085336345455949476, + 72578184704825685027991968892, + 432901336590547054, + 0, + 33216920652689465209887352797, + 4487634639283352676734120191, + 2950422356357905041, + 0, + 54564780047414078911673701027, + 30025753572266691298224127448, + 685362937843059483, + 0, + 67138621943231662809856667108, + 35536622086113408094707128679, + 190411862878876857, + 0, + 48142852697660130038572915688, + 41323903346290873181192498806, + 1662095782921040824, + 0, + 45598669735468506202192834317, + 6833778644544063985691963405, + 1883517276266881909, + 0, + 75565302857262833598902558343, + 30713909379295351298490249058, + 1168873237244962357, + 0, + 49392395848193620327757704366, + 35210055875983544209068979907, + 1725158095484070206, + 0, + 40379383230020660349172635499, + 19367882033784143373084242959, + 74789987621199271, + 0, + 8239497945502984817836727349, + 65383457080857950278698762470, + 2211704475783447865, + 0, + 65088080708861573014605993124, + 19658789254405276068044175139, + 1546928163739898959, + 0, + 44391492897112417414559826228, + 51091256463142858516392516045, + 2322709471740572275, + 0, + 18661010978786051077459718130, + 48955598396292730136701179437, + 780916620951003547, + 0, + 36122430916608977004901867258, + 54815738602592808713282521827, + 244562605560427525, + 0, + 17493069104288769231730927298, + 39925262063930014711092500044, + 470411241745909751, + 0, + 17153716773376571832431590856, + 76197999709725477246042585094, + 284026573044249820, + 0, + 36106041238569613226434622702, + 68578437771354527664487579423, + 455746190492315201, + 0, + 35951333349054018441813831092, + 31692095822474780740444559601, + 3303478619465910444, + 0, + 17448164149078974326088144510, + 40832368384955184149072636478, + 2779549211226660857, + 0, + 9845332448948485589490842538, + 3582383757646077665279845320, + 1395951710319336769, + 0, + 69072020035065323871757315612, + 73533616361550926150931810210, + 1009811762168673547, + 0, + 66238941468724887674269619836, + 45114736853736751496619603101, + 2694321423254487913, + 0, + 53640695077341833497251048313, + 70097892758986302083143685028, + 1331119744553691878, + 0, + 25232148675748515283237371883, + 33538981593629359172084245353, + 1066146222249668352, + 0, + 16735180152733870465930876387, + 19931635780656401797425589360, + 3402928052867193376, + 0, + 51119762067819392106284990740, + 26792704312555347227696681970, + 931774776387682583, + 0, + 65581965958514777034920712517, + 34748600687196593147033756906, + 943609309762223649, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 46, + 62858022637545769482710796336, + 56346580081847405348827116884, + 326593677222538131, + 0, + 63010306283514798573891302982, + 47651346567180615553735913065, + 297960247215097105, + 0, + 60108644256308501462468306702, + 57049690292565910712855943774, + 2642937583057905353, + 0, + 29663810122972601617873576531, + 44237648265655406952768293179, + 1766169704803867014, + 0, + 53575204472861703323806428336, + 10723129145910018135591281232, + 2845821257931640545, + 0, + 40469608770848945727326307523, + 15336736201144088770335554730, + 2221710866682133058, + 0, + 44280801305601824044728005228, + 23615443082608805519499972509, + 545274610571193303, + 0, + 71029482644893734145563033413, + 37477691446451260023011793811, + 1861030352363987257, + 0, + 74476842886676847797261228393, + 62909137850275465147878980044, + 151900790100043066, + 0, + 7622746747834786320254646365, + 45252274136728092231069942716, + 1873213725633757132, + 0, + 31741493628319905727353743882, + 54813976148723710594121377113, + 1256987077555934294, + 0, + 30326192103530581906546076976, + 72974174171640068313286588340, + 924345328662918278, + 0, + 65773086942227193581271703472, + 13819719641683081254707200618, + 1426729569469995221, + 0, + 10922624387358428082537875181, + 65082593260157538126878227870, + 2133844101813618911, + 0, + 66088093700301775887288342190, + 58990846080176292923932456976, + 419368253555899548, + 0, + 22328384112144327859081771555, + 48626379443079974458535154813, + 2457707603600808672, + 0, + 16579583356184461257923949941, + 38607372810336506434047297958, + 2471672125827927866, + 0, + 55115957761943353784525967944, + 58107220008134633820049778004, + 1209683629331138652, + 0, + 8199330962800654532574476768, + 35834938467368876538065073792, + 796644577922762169, + 0, + 3649569422342233176455893228, + 49160322365466402078427961747, + 1344845686850523246, + 0, + 31466249415897313701889753738, + 59649951646414989446777371442, + 1288046073071149377, + 0, + 72494316597182190305546207666, + 4594509292904671712699505173, + 379753318609242608, + 0, + 57794497130208032045754890223, + 6739457823655997694523863082, + 113011116110158057, + 0, + 73236190382516319328005667503, + 65915167499597936981250133895, + 1647289829790505922, + 0, + 18962900119048332060219199027, + 20199048651410935260685735423, + 3180488765805308790, + 0, + 26501588466668496676198186221, + 43492744435610435304546060759, + 2706176198603928353, + 0, + 38359777440701217924929596184, + 64685091979398108592864993555, + 880008661232290558, + 0, + 45558530661584759086845728455, + 48936187922284976039730093575, + 2382646996170462640, + 0, + 18025848931003530230996052986, + 29271061703304141574886447607, + 3338661248201161406, + 0, + 18539685039719579274512799828, + 56030331607434269973449422170, + 2853036631006001394, + 0, + 53319333878083919075933426901, + 12139714536639761161506183009, + 3102051034439960550, + 0, + 72620049843978680383057735394, + 58263230838132560063533604257, + 1085840147131745707, + 0, + 30390909554445742082166808009, + 57957442908820686204227772566, + 1969338400885652540, + 0, + 55362705872894273422338636041, + 32932798481237283561897193053, + 3452471453148421590, + 0, + 24755070670598162846322699361, + 9688021451093755027709462063, + 1108165040484377770, + 0, + 73597804961420627475946099172, + 9678509521138596258122574142, + 2177064898364233252, + 0, + 8219846701037483832402591813, + 35531161786510397591499427001, + 1597583615385664556, + 0, + 6175281796017843080927941255, + 45805617586822201877746739399, + 415812148088753167, + 0, + 76390285817683742743214054961, + 67319831350137811739987238966, + 3088386245453904858, + 0, + 25630381380267323456925021219, + 48070321298646794770692318015, + 1145061673035495030, + 0, + 45802889176442827525668830154, + 12230881818418939331802450168, + 1643267217068633437, + 0, + 56784235487556155055880812151, + 13732743520618074494225197859, + 2272840844774691244, + 0, + 27627104579542910879634266045, + 12592220170874079986646767970, + 1795208794329781485, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 49, + 34615426202246034351975701367, + 64782404700866666365292167628, + 3070304826334234710, + 0, + 78746227200420037014335610082, + 49735968074758845002728535790, + 2932088517917290917, + 0, + 38421962253537293647949934418, + 25486506500274535640386186296, + 3182978449890772958, + 0, + 5374203235290154541963777396, + 66774402279403329543837257764, + 3451934379811686446, + 0, + 6210341885996341494277177234, + 62016398295957072959255348748, + 1253428951642925760, + 0, + 63691623531048909053276140437, + 29179854624878176549042414611, + 2593368747187793256, + 0, + 76380119962381658038580217668, + 28810022716091185588877465573, + 2653677316169891793, + 0, + 19709237760507723491400168095, + 35348849797386782459707923344, + 1255289452170051897, + 0, + 73104293311201954187880548547, + 37647132424747814548490484345, + 227844755347667308, + 0, + 22203250628389026184584214179, + 7864097787073553135304477835, + 1201613271795693725, + 0, + 3093431419028343095083704839, + 32288312567770545960215443423, + 2063318788361408681, + 0, + 42937936787717469524866882927, + 68803462689290926348105827410, + 2434339785772197586, + 0, + 35564901722043670445335606106, + 26698054419179887769262800226, + 152464123472442225, + 0, + 55549915067714138701314536695, + 74970391948123334422292345907, + 3433611486662958268, + 0, + 19323467195990760866201487623, + 15016828814201601483206794602, + 2188693977709562769, + 0, + 56736104481953873115254563324, + 61734907814801185298803887943, + 2547096934355483591, + 0, + 48164836488644125573236371315, + 71470235522713598465797905611, + 1099309793012222826, + 0, + 47786630834970945419545100345, + 51082207575431539830528398953, + 2504691747624164657, + 0, + 44634141438251649587938050128, + 20731725934637234125025454259, + 1004136925227177627, + 0, + 33769303906626695378504892679, + 14374774512909493296552077120, + 962713171515086183, + 0, + 580458062901511154385674883, + 72712966937539981386964198256, + 2848916499840734544, + 0, + 23826036809837739649153862706, + 70636065141376222528298106111, + 210033307810793263, + 0, + 5539709051322904657979499691, + 10683623377353928752975718448, + 3350571802570280778, + 0, + 8280494397001812062622840142, + 64937424745896494104944692284, + 2107887239130479722, + 0, + 35635784368992965698855483544, + 33551547942358385617283046642, + 829990130989364623, + 0, + 53876306783860253007709154643, + 37144138556576132951321735581, + 2878944319900796418, + 0, + 29677687082314752645750907001, + 58557081257966239998502251605, + 68921869236974766, + 0, + 31898524719471726873437253316, + 75979804672848563633378580700, + 1067456071833487842, + 0, + 14999048430159828842223694226, + 25649680033655027020803839950, + 2280615980421181536, + 0, + 64546694047589313932973979699, + 34596595058045325594246296096, + 246944930175330864, + 0, + 70122161298915766574754490698, + 39865109982676149094197083711, + 1632996340083753010, + 0, + 65212392118053479090661785028, + 22866564373863378926113409089, + 1611801177037373131, + 0, + 60699056826672002203615163728, + 20916979366107878008666639357, + 3406850566537699753, + 0, + 4850196895834672457138767540, + 5382372185080213175957004702, + 1648677673118291062, + 0, + 33318032423025658818128521893, + 22283303961669521198447345638, + 1156603054647354843, + 0, + 52387062263331903693219248833, + 36794334795975644544272319816, + 2845915639168960422, + 0, + 39964807553821066225017168413, + 5049723698295720253292136547, + 401518680581212601, + 0, + 59215249004320237310921408718, + 77605614437717406859287180324, + 1902312677177441739, + 0, + 33216297385657120951146852502, + 37880113545551426611760826189, + 721252162114707939, + 0, + 19472463439758265541726363728, + 13010951462100576096801012282, + 2442110781447156045, + 0, + 16219650089881923452239491438, + 14205558099764766710810010859, + 639445806913768963, + 0, + 31197715360374760024132980339, + 1514336058331561557761795992, + 409975884733368635, + 0, + 16735180152733870465930876390, + 19931635780656401797425589360, + 3402928052867193376, + 0, + 51119762067819392106284990740, + 26792704312555347227696681970, + 931774776387682583, + 0, + 65581965958514777034920712517, + 34748600687196593147033756906, + 943609309762223649, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 32427250892924671562308045954, + 38069847857764728027972845848, + 598031728157175724, + 0, + 0, + 42725722093098403516454496780, + 56643390867050116870715439595, + 2123176560608706470, + 26876643061186236977189508925, + 50397476200957060738409523296, + 1846725087644701747, + 5800310349856064543832005641, + 62872126388569277319007954968, + 3142895529062546134, + 21158387452519429749812080159, + 29143258870701027874684406821, + 548212402029116923, + 46570462627941827259543839463, + 76579677713891805083632225091, + 1864030999157858874, + 69075039070498595203357901735, + 15369700282102852728716294188, + 223040644685792287, + 20320846347818318626235736121, + 69664666779210394672447087628, + 1083063850180071546, + 26202863935421453719995549018, + 41295549767807435178717068531, + 1584503098543877871, + 71786609294792293093674210610, + 13523239230918465385902554245, + 2257205167210351470, + 51517391190656183002541620298, + 46392459487677243125595543731, + 3140805576992650756, + 49660804518560503075972724674, + 16760946088439269452418708634, + 2666055252388735758, + 53897542150586602491397431404, + 48890876078037412579695968765, + 3456330848876766800, + 38715927838583987524344516316, + 16494791581028558212715820191, + 3035752889575752266, + 5685209927932797384914398062, + 848537249965310431897710575, + 511962774745014443, + 35601908562754754994326315846, + 36867921208931397135972552055, + 1984689029750796680, + 40721512686555355496037176760, + 61511994029571519235853682558, + 2058353054115910825, + 1063301953568340527081939647, + 14787006057519229929707256806, + 2107537815364102058, + 8011544823580146495300154179, + 2929362965579097034240378182, + 2691053575666023240, + 6708665243922626891654933309, + 51221403384755410630116382470, + 2417644367645685884, + 71669289133209159390798004901, + 65065525113629890170482936251, + 1460638513547272334, + 74233002654968534433189005913, + 52313592416332330107416656261, + 1713406812024740599, + 15673444199016490302972242672, + 19872413957575582025993315919, + 155919089531813519, + 18615583060317656461008982767, + 30344117867583382583352620732, + 1370771490497659339, + 70460111407310609558886970351, + 13410782546739102280719123686, + 2239925694206844784, + 0, + 0, + 0, + 60184622296806572439997666907, + 75008714795759192705677030670, + 3262785094589981400, + 0, + 0, + 0, + 0, + 0, + 0, + 50185732680358774463548804626, + 10607397758555418006709821705, + 1206124784999809577, + 0, + 0, + 0, + 35, + 54426548797550874077908378750, + 38757716061315673285883152756, + 2859133475959913970, + 33928194857291725719872533805, + 17957280581119598668969668696, + 2899317194808362643, + 42203310624157606136906066023, + 45887773411799213741857522043, + 857090507024822380, + 8331772880609220693056220919, + 50648520896634077631309651131, + 969359585624209055, + 15388211542287973857906292843, + 14498945773090400254367445454, + 850252957551405830, + 10000080628622450480394263163, + 71089688024268091580985215575, + 2453685574232408285, + 61266770515376034068732812065, + 69114000355731299462884956046, + 3125694647736772392, + 50808364657817556755138775811, + 67001536905677713485655778299, + 110062768890430963, + 54391739318669353258159502218, + 7029076289089575583024273863, + 2875329558900334621, + 73094986020163840713289374852, + 60273476092108804566690144583, + 2240222205050822265, + 27729844764555042882168253693, + 4170411463762952566365105351, + 2308603378956025424, + 52185981973640551340944379992, + 26900000837661188890154405745, + 3361711790581330969, + 62047664401994366474067228629, + 18429117125045284918871517638, + 1303721604211979262, + 53783850203744141910654527211, + 38299068218552588521744653905, + 1504970172646263220, + 33642489205963815029991828351, + 38973574261345742936723444930, + 2416212724216872312, + 54933429759365359457397515976, + 71143488740095200883785177891, + 1116744889107512209, + 37895558845662478772064249258, + 40031984396577609713518059095, + 1177659187989649208, + 1965312423645654836412503089, + 59548634558944066428428460457, + 1067086750824312673, + 41114239717407679071316623603, + 30099975008970545221266889533, + 1972477109726101634, + 77888505063255598701813655527, + 6601491715460804424532315630, + 176406995355305381, + 7697485064344617361298203399, + 27918438926649594449019902888, + 2428300506363836936, + 41711749366470726199892005986, + 20175166986691006659301832246, + 1579422794438726067, + 30642153794308174877947464219, + 78115466806839840992053992000, + 2987818556901730265, + 48073492605186353759021097527, + 12496274620185380760693323527, + 2426170430941977498, + 32192409916610961010424650066, + 72469041253879980981976344757, + 192591009164563125, + 38355632919605364939468917894, + 9266234054548192669760997495, + 866421963994232073, + 20357849710521287490169055266, + 46408007829040031575006686982, + 3051745858639330937, + 52491842252152308351472628976, + 58082053164459419054194684085, + 3180436217752216068, + 12952417682519310007536553844, + 42618120806473186496640197323, + 1960569363877589482, + 3748722958390102740552120189, + 65661815802707245428917955503, + 2033782397220232998, + 49304923264677430930618322843, + 60753711436689272624026278711, + 617751452946817968, + 77765507210105852159522335583, + 45931346720263784744357504978, + 29966411675022366, + 10598659008350213245736026163, + 40006948968277706871390430830, + 1245704296229732486, + 14823962527536274606595492477, + 11068650204049376741911436297, + 1766170829127759457, + 58228387613949748839579743219, + 67945109917426861171867961213, + 1620570924312554497, + 69009897449058493625506334031, + 69172039427253086383587813857, + 441037584957553699, + 24611269253706943505541355108, + 9023157886405365210122926139, + 1457169139286409605, + 47912546949246396713659567080, + 40311952886040547459108846194, + 2015607973408089026, + 73391381941679087199895436866, + 32699164472376488888603007924, + 3210907056673870242, + 54978266420764326445159663752, + 13721994634428809650997028024, + 382435110405552125, + 6991420081443357057579742442, + 14351071347466430345459216539, + 660111879516814851, + 13503581979153938311444537327, + 14687138917645010349727468517, + 1165590065317272243, + 46915293025611645413767674861, + 5639254812412899265072961270, + 2859864496425526985, + 30126212928655184840695089246, + 13582785026221953356002455808, + 2416496324843490400, + 38822937132312465214761075400, + 37606686172161754444589618791, + 2239597819531017522, + 13311830695108813453368800336, + 60860861933012196145870419611, + 1858702461235321337, + 40643091253738420585906028449, + 20211428832820263358434669652, + 1038878936706606849, + 64264830651028770871571760948, + 72872522860023665617723425012, + 596352020837885219, + 57863759820436370389567865917, + 12714857689619189508052234970, + 2771448201359637159, + 52485553701809797434205303943, + 39944318959338172429067080849, + 2571754366286383401, + 24826531296920649436814051292, + 47809725993857077982417436832, + 1438690177270632250, + 18368563497895892462667550164, + 54734844763736769164378690550, + 2790005934027779994, + 22320581482326847581942263564, + 48070598372733250943828062954, + 3266471833120892687, + 78201616303099181507277759158, + 57256715582383961938636046665, + 1099132779429509451, + 61094189187168743766597669197, + 70193627970738288881204295914, + 1982236310349753488, + 53785527760068107225437829534, + 17710320544756410202997230800, + 3004754808658453881, + 66201320147392180365107497406, + 20493391775261137767450283426, + 3442823397727811467, + 52653206652728399814660386782, + 27462278272083187369849013307, + 585118801712541892, + 58958106164582806628664997590, + 8906179752178232387769013140, + 2949670425328866103, + 66640816103870930569519783657, + 48408289411499048881949788196, + 993196131636108341, + 24747357183927818693947728365, + 65660297371829949370301778716, + 2643784711511920406, + 24461102690734823232807085453, + 60310057942249929187328830834, + 2929604497355303939, + 16817746923483265653099239413, + 35692334731128195111198850469, + 136651980570236559, + 78998203443301464024123059015, + 36517107204535109350847293198, + 2642229837382668831, + 65393329947470530188391772311, + 35671093560219148965234291091, + 1174279489650212279, + 25430198810377996145876056372, + 6669388829935734458746955681, + 1872122325355613887, + 61909377841190433525661894569, + 23211023101001214653218048708, + 2629298658962977547, + 12229290990276641584274411013, + 32856609429948116280670875189, + 1299342786636136216, + 30473842718092776526944436929, + 47000069990462765709124641035, + 938813044893076569, + 40482915845060944605944901080, + 19308184850688434509814377718, + 1479532783477759985, + 17121088095027205160930929890, + 24107399513244301640898607135, + 617835475211485513, + 54471882829259018711805857705, + 4249556380610798583791541471, + 2406252624853541088, + 42261837806714346925549701657, + 70490206017197978900375821916, + 190677816566118892, + 40609503705252049745209835951, + 24201125316489291964639755256, + 2050714319455632668, + 12193009004809635468192468175, + 31028389681492433219552741138, + 825931905431868511, + 25327904645618853020584198520, + 77637829810396851146964121865, + 2825257693600073337, + 40183521894817686668606925350, + 37914790047010503430989931053, + 2921435465324495119, + 13031354364708814516695893574, + 7753027603689983356844777254, + 3447537061491476052, + 65944514093476959076038274523, + 2240134819430722747660242581, + 2349011386678569156, + 53472374537382078192574758633, + 51015861021864924154394692283, + 835420285866676718, + 74880498837892057124664468131, + 46885732971652789233270701843, + 3265192697699490460, + 29298269800772317282474838591, + 69311155241523815201725497491, + 1418210139400868952, + 24676391918477628234745333803, + 30655773310246894132058331011, + 449504673812333857, + 51433230454272153876570680186, + 75850084836315094744055375853, + 3014468889003570823, + 962079878234197179595800055, + 31965725858679307423095291075, + 1485851962953587964, + 56299151410890101367410810269, + 64032626349166065075254981086, + 1113848600781504123, + 7928623713065598343332198284, + 57586648708050914354530667786, + 467505966684728382, + 61508516304334574841277668984, + 58484453817963359273284079940, + 3351082645445029697, + 64590675661487233992703674955, + 75126845055891322988752815055, + 3066985257689015819, + 7717083658238859004927568800, + 49746019736123960705766443650, + 1460532043474212836, + 26961452732584369424643424762, + 52804497272358908114847059270, + 2907164208591973809, + 59738537761844252188480457951, + 40800560747439450097579709394, + 547205706866521920, + 431946553835659930425266532, + 47580340966212377000885945576, + 486248839498106259, + 65095962399675103360037414625, + 34142883492176075007835892670, + 1942435940088567524, + 27595454815534208148395056657, + 28513265332089656318163482948, + 3170026655120364197, + 6585705177147306207406570229, + 47475502458943390312409762419, + 2177857754237875615, + 12999377279934214691572216290, + 64376656398266419910122705719, + 1091488022581396327, + 17822904725124120444108025271, + 36554324817942520423571818030, + 178642833420582207, + 68202531515202131419182196772, + 49526059634092608537642371035, + 845020161911559686, + 52434348323068021763836733682, + 27582355340874497523363393917, + 962053804770685961, + 32583965087551579935773805359, + 56063296442823020943510287192, + 1529784611341435236, + 48491431696947239385507935632, + 53910215152317597792145689483, + 634667045459036908, + 10222832604529648079861659216, + 43102486092685322476517322414, + 1643294334031005139, + 53455876970886157996419809645, + 56064528769055961578433817424, + 1394718639239221546, + 71529228906766722089621839337, + 72623290287506228892866958106, + 2334612457556281180, + 47315071883294498025422182478, + 17038299583573959496593256896, + 1723017944547784254, + 9139894168508662133480706180, + 12570068041080739014152516205, + 2784667675060556756, + 36616170784809338403173000600, + 57650989702986388826513276453, + 746879459172173138, + 47858279673993961718839957692, + 27568516319910027492183979808, + 1513670383437258831, + 36247854720074173983081742450, + 8710527038232464438616139467, + 186174801967941508, + 33650216508004293500007564967, + 28803814356114359544782685874, + 3316150097687469656, + 55792168750531234390521627003, + 42650230832903938857823378561, + 728508569839371515, + 71580781323333282522977735767, + 1418471826119635382178183658, + 1584388984750124097, + 61977182994602163639528574283, + 52398533175672645894238688793, + 3161802164101570953, + 40827596524247662180736332344, + 75786206691106912629033869310, + 3060763047975228997, + 20781403243240520713277529244, + 5957444702493197443432802748, + 2555722208935491631, + 15006997507886025680854738703, + 74650645292172768021220579798, + 2689652944316092442, + 17414967321725882568270422846, + 49156077731349669384170054075, + 348931044753464990, + 70834297505390908081152174958, + 9768750039343723647510949541, + 2918681576091396246, + 37279091246783157084215516395, + 56351148600933087403805774687, + 28525577682414200, + 43146428002288805257562156528, + 61352607700722955385751452503, + 950262069267356725, + 6609113083493257282137632070, + 62541151924456067785135450687, + 688666871622090217, + 73896525052724169025000015479, + 78019477450816058005892914038, + 2566592835904246076, + 52535584247012128596817359958, + 65213060838982668978784109208, + 1646222128378839159, + 56449700906448882057522409365, + 48308501445030718504127843257, + 1187362181684573221, + 67746395887227876576917571677, + 4152650419655264230788167861, + 3480200313537113889, + 28655517099011977583271806349, + 39486893442085181950375084064, + 1269482852609069591, + 70042741180533743259113056065, + 54475481300292222953009973544, + 2105056774029421002, + 73177491399878636608856237289, + 30267627509067968961942036766, + 2082724123883517338, + 65243777365118553179179694641, + 18983970238411203608004575390, + 533404546446158152, + 36597985858549138302758906350, + 23129813945116440598755838319, + 1749447772507599461, + 70970499877112765822510862872, + 69179866380942486050839811403, + 861879256526338253, + 7325597238371959187163295082, + 77355935767225410427128546511, + 498505045627421536, + 65271151424385793151633548927, + 48109723898631390101119303776, + 1901048388319238901, + 67539842125536686461434967797, + 67740115819813217022608563970, + 1116164815108408332, + 69925280496503622042557781798, + 77222766335132045330385901620, + 3349875574292251405, + 11122015865354461210589487242, + 42315923247191036603249142537, + 151856645204858997, + 70119155229481667568613341446, + 54934694409573414789153629426, + 2132659927213569793, + 41451410689811560798417458613, + 55443886373065491749832958682, + 2978251866332432546, + 5210919407526515856152001814, + 11602900776674005691180928901, + 1075367439363802702, + 4209830535523926771291305154, + 11341370371244386890208446215, + 723259507545680591, + 8656830151795006509134165593, + 7749348735396329180434791734, + 1652131399298161975, + 5765915274987479900562177282, + 48773492529895393441375749990, + 2179776163506801474, + 79152253453685852667621686451, + 40236121595509821194780212883, + 568114027595693686, + 32921277791149576530955360025, + 46543845781627904707882150079, + 389419445761298945, + 64276631490335574180065525945, + 42976508679820773760520511781, + 2692305637186800100, + 48004128979495633005868128175, + 8195153894232206443670274, + 985602991351730392, + 76026945439463413514322032296, + 15425377975818708984013334672, + 2428544198019721863, + 53657734933467492619205738273, + 57132315758685918577612530357, + 2376737599441720561, + 43444447161370295892458763744, + 73836409677209668814511952696, + 1612381787012741453, + 58104480111824641585430900834, + 78549591026402183759246550789, + 2515387142624883923, + 36256834095562478179855993061, + 47436098398542471014128746174, + 2954426342226204509, + 58277857128661229808543079472, + 45044369227684211141103685538, + 3024266201912894476, + 15198419125139976815818765757, + 1018588268556024785435825210, + 965921580648027209, + 75633130984034301983015349272, + 48430111143892265681922262795, + 2238797223053827807, + 21499329689740815678569140013, + 46987304015141374551862133436, + 2144150576405937088, + 53060030740523476756375055518, + 22903746909732906351184002373, + 2561571587115876584, + 12280523955475304612368994549, + 55366334360702596797109686659, + 1505073945582760562, + 22775326964165103142471769437, + 40687399706266098817055350306, + 290993559653994690, + 3709669280682316832249139423, + 62175592681190648082555393604, + 2925503099376800566, + 55852080830817791419884389778, + 42808273794571147005084371335, + 1202483214401515710, + 75868203191808078325617872197, + 76456734889305254143181587867, + 1995464024668878260, + 53396816904139546406800571125, + 52711233502786361054049356087, + 1780653767235138360, + 23732121600538658444265023611, + 1993646861469803489027399863, + 3255028498732414202, + 17518660999930671641333158247, + 15762489392507731615773939299, + 3352184300414363981, + 53720552633198524338838977703, + 58557160350009856274519334896, + 1322035340815278293, + 75133319891341039535968449206, + 38428904083114986792907517761, + 2351923476027224263, + 51356443071981699610407903092, + 20295732219481990829326300743, + 1534902846297975329, + 24921659787806125922025659531, + 7866951260695866094685792819, + 1444525252952950180, + 17982862260046416345231461974, + 39792789154705191700909864127, + 2717335652139658235, + 20448714639597588712717487590, + 29232217675894670731566469709, + 1421914548682576574, + 61906243934712925916891698713, + 41631121996540235492688808630, + 1000996366528378181, + 25371763956475530846046770099, + 63432395120221127496966485723, + 2272842217376547972, + 29111776857855934968478457141, + 60282817533101365189269931340, + 254869729710907569, + 54353203904975441005023895442, + 12231315368062781352737644468, + 2816889141078034567, + 41498112612758206122947673154, + 49256357032374143488234451404, + 2181089011579663432, + 28822043657163336409005820988, + 36920493733738767564024816911, + 105135211654521520, + 49417197051924052968619453553, + 34930295371548807366955361072, + 2870647357252557218, + 30676495051221587209009189618, + 49173333895517429731527426344, + 1617602729347124452, + 55704983698436970916623203935, + 68900084194140426570260498825, + 1621630534039473043, + 29932357891287966980394323912, + 52573974265426254185851027201, + 1492676734160022626, + 41206120382089581845123682103, + 26668733191140275324387829829, + 1281126945471622041, + 47487514660640615793817143932, + 23590928987135970387419354041, + 2714132676284699319, + 50984758448778178541375159057, + 54529485054420513728007158396, + 1901870167438019469, + 51773846129590865095335121998, + 31129064020518036434851171225, + 15011171286343580, + 13442019771336012061721529331, + 34147691848441609557654709883, + 1313917299430851454, + 49568596281546164943582603992, + 46635980943284741229154009592, + 1569725398193190054, + 4634371303069473073176102020, + 16487736730865372411773109036, + 1159193672578235394, + 71923262789794781321764665494, + 51745919804716412482323491415, + 1782822977682177814, + 43734488273274594906720306805, + 49506331579354468019799246384, + 3049096197167481174, + 5632340299139191102321387336, + 7208370563502688642609392965, + 2031405184160121777, + 4156192867044644243162200926, + 49741686746562096136834122503, + 1636810786980630253, + 65550165685529254100669360451, + 74698787975472163056428117799, + 1336940648502902737, + 27180360997881136442847580964, + 42845445032019580363419654918, + 1905262145146561545, + 34030860479258068081547196625, + 54261723229458845954382781681, + 2903213184799149308, + 16884275374979770826958723231, + 23842825322878077742908459516, + 699940115037607469, + 30359080794871026078594082196, + 74074874778037953236999718759, + 121266311308986908, + 64041382779695911038882896565, + 49808305860259824007762332819, + 2884909353616649252, + 27545750113153904318605916503, + 58586029941375401020124563525, + 116323617893942033, + 70281572847205491341338802775, + 57752210304173180520354797485, + 2393165073362397847, + 18079754363747050207781749760, + 52229914113433747549304256996, + 2617584176151967849, + 71094126078698231845538527183, + 53316908255047336060765894275, + 2180479410581954425, + 46708663387937330399527642120, + 15334823086610845928093795626, + 1721343159663087823, + 69746634432051203490768530012, + 55498338657563028549726492591, + 1234603188483330141, + 71697435835229055130969676593, + 18455826352283147777717815556, + 2123011488996691496, + 27140913057237146444315511694, + 20414759125638052578706970083, + 3365727964591778161, + 401923129110356450633827212, + 30254548529594563305107180556, + 2754245852786786629, + 37341351827551290885300283678, + 38669844767648941316421619221, + 1992266111843637184, + 33769936650199931912176619054, + 9347100847496046154579123122, + 1296406101910504954, + 15528793388030156878347228936, + 39280315834346188719734441320, + 3473547167712106767, + 19172702355094885154160761663, + 15601072688946463960988878829, + 1071263610903469507, + 7027291399413088506729325453, + 77352810229809229159859704267, + 320985180457086421, + 22376123341677562412726153133, + 70840794117192427737821477918, + 2373978773997398172, + 5701658117308829176213948487, + 57703030205589941096567979524, + 1973645782128269004, + 68772963160419032649320604971, + 54819327245317689320414132224, + 1402713543358473988, + 30995511899533603044949352652, + 61728010161713671909842507033, + 2313844686395933301, + 46756178830252851816444056790, + 36707914309354619174488129355, + 3110147358415734531, + 78441270869203106176612075802, + 69436451409807594036306580288, + 1064835997824160155, + 9034381934778959413474220254, + 52511163778260474581347482943, + 2945579067725082647, + 5129186494087484903430244014, + 75038704301849143828984898584, + 1552401866332458462, + 68113718863929844838132211311, + 26355696085741023782380849764, + 28255919599132452, + 15377023412714062712803204023, + 60130913774035477145086750528, + 2388980859747547418, + 21981068097886836546588765199, + 49671930939105082753438463449, + 3045701122962006947, + 69792995664121861759227158405, + 12365753703132514577155743701, + 2921100895122577957, + 45172399514485537461695459047, + 67957883721936179878514260849, + 2014789562171851355, + 1891963327082438304794331855, + 21406647117041187712261995162, + 1643284803163866511, + 16477591928494953154069693913, + 3640855109128321946749115373, + 1608558146709579290, + 16408641535850307830546006128, + 17591857574382179535532537945, + 393835356583596907, + 52903512304444377154059733230, + 8825720507392833705956316363, + 642640697438218072, + 58804476394450590399599429266, + 68473407060639251987502337394, + 807054818301377775, + 47358990178126763051192303627, + 27789372415953096687930514704, + 1510743173214072251, + 47222983275008349584870466416, + 33707101628080690749084113479, + 856028080821215201, + 66772565525874573268278028942, + 2869729701378446080174001711, + 1393197475005549272, + 30055647746296416795669586891, + 14277333715067864891160063060, + 2159088906430807046, + 11556407557304093344050241721, + 42422592600465863027932360536, + 309784807924652757, + 23246744480813389838168401780, + 68331881325412984952733722565, + 2992225773335557274, + 49469654342181951421775630857, + 40395667528046918020142840574, + 3206916733552562668, + 13549106590492012036715564560, + 65833872534801906338731013152, + 1797703848584814972, + 21872992997384177125776137102, + 14981505599978747864441197337, + 3269872909973492454, + 61181032437041845171344493747, + 65169376756862776060817102057, + 36839607357454223, + 28813532002098672697017132965, + 17122224987657687508743186139, + 2492102721730682205, + 5788816445699396410416444077, + 70972408656268686961127697487, + 1696646351435103403, + 77945587734700046801390353316, + 73704222961490755932981347853, + 3483334061018520927, + 45543759574460590886184295462, + 53438610795419001310898613663, + 646710126630481975, + 77498234926348839116992111198, + 25801354879080824677640726520, + 3046966151520136094, + 7908632804618886954959483092, + 64884338808795515196915019974, + 2390702460210420899, + 69270092124151635370354982516, + 56211562625403019211146041704, + 844555670136023216, + 32581371188228977177985828809, + 74073948404429668144835253107, + 577205414884847294, + 12131326279181611597071976809, + 14652907416830680279271997618, + 3439696664975054315, + 73982002032138909339908698023, + 65423146980255921124517991937, + 936188057341413207, + 50794286633395199424997419979, + 72956959150629783525384179608, + 544061514361719548, + 68992437661107696473362349164, + 30415429478415811318830635271, + 2311002522391795047, + 38178562771083394898718974639, + 53000338718915768267612375710, + 266887138267141637, + 34591872451305633359800687086, + 50040031991752910122705009783, + 1359690561323015700, + 12465572929327958096882295990, + 75900650690245352499757792002, + 1470822893890808404, + 75547399832573014377458028791, + 32894843350797417456628092994, + 2824383353823285406, + 23859011597380052413385621024, + 25130410510257045378095085247, + 455298809021565354, + 488352399581174823683686543, + 25952339207396173092354437206, + 1275197560841388164, + 76036410237847168539317882231, + 65956080333537444126285973360, + 1777582174491258240, + 60926065838453854258697023793, + 62217054451590148414193827477, + 492469913112819814, + 45645820875375071504360169072, + 24134071040329947958080372304, + 1553026220478665740, + 6053478557688423529436994800, + 48782323064294429515456023450, + 535835742909114315, + 67719186077686462099496659978, + 62723391096816331856574728333, + 2709882640738941974, + 74781965696877563995178493163, + 25143205157561835556618770266, + 2082343254560032498, + 41646241179300823496599975604, + 70145472319340454505733864666, + 179334231856852774, + 76914259488378841088442899748, + 21974078222675734098985436952, + 3412252585595053998, + 14323558955648724101035691961, + 6085010351690952556227173524, + 2118533143860983129, + 23078657673429117798631792343, + 1604248041770585772584218247, + 3043199907648277108, + 8360562494248844598785365750, + 513164581882624906512055292, + 1986321818908395448, + 47340949829259057037288420544, + 37657407100444140672288280157, + 664054920868376261, + 55298883444927900119652264431, + 954354141401136618674755529, + 3119871306746799366, + 55240720253668711709194109429, + 75950577014488392881031737656, + 71234694128390214, + 42097470388959849543522809321, + 11618090088328940681354974146, + 3201782014140170721, + 5079375748068255050715403590, + 67347690644657638835894303931, + 2009294710973554, + 27072718469073421893146517422, + 55697962734961018548455666702, + 74954944207162661, + 41858003219549723432659082999, + 15681754213753935033281590901, + 492913942319869407, + 9662272440779872069808953625, + 13652240923456523908678418676, + 2672753531514951848, + 47902406384775650741494609151, + 565985032712162512663541481, + 3245424492517639173, + 6384656315307144532270814988, + 8615113282863993186535535796, + 635376214383208201, + 5076883384124918148992958586, + 78528444196686103346435383323, + 1321104668414499734, + 70167552243891298793200001887, + 20063848964432124691984818391, + 558384725391568014, + 51788523955440742131847289196, + 2786886378905442236647327019, + 1525065841347221668, + 30560424150839799154758865991, + 59600122144481189888929797463, + 3441550459498247645, + 26777663195635139282730011270, + 26916307975643286924422784312, + 1169440065272895175, + 37724710437828486254096259917, + 40793262299547082753018957587, + 2642591919037863410, + 50952638946734773176706521570, + 77985549999526194504381868010, + 2928244743743761886, + 53422880358459731490015893065, + 31554163667217065868693513173, + 2460692460815658206, + 59623930781935812513552669295, + 61841642450690612661803298721, + 2480342277568673172, + 5126845830238449601649783152, + 50554473802663791578012632209, + 32261907514807092, + 78643958153588418125859390892, + 64449686038381812747465619866, + 527252011379686128, + 32930085835386848974897216587, + 3762085238087895576653455298, + 1002520327983722003, + 12940355057775632714882205819, + 31554465199003227099698199694, + 922662856973679864, + 48990801669284699309666793131, + 17463665389750083315509653040, + 592574493340128153, + 62745893785262246722406330728, + 44236993472367568259883196463, + 465670251014058098, + 38925867033927487185670153472, + 14435390561729642016789180999, + 2387579556162570622, + 57464219870681329155248104773, + 60291570332183435707794768996, + 3022425433694972753, + 72101503987491654774270992180, + 66642441110705333233968104058, + 528459576176923345, + 20527695730574330641095322121, + 49740282513647597798086927574, + 3270170224024227689, + 56740987364234138158791335387, + 10326630453972742427621371890, + 679566644389066877, + 23704020024501368300420038316, + 75427502482998620648373208899, + 3375297937867878616, + 33888229640429258880437899941, + 51574402441172009233859748146, + 3474896502775322089, + 67142926750304876429794047077, + 66378055323819221762516638085, + 2940365116601795328, + 18229495999149650282236633807, + 41965347090900218908315509821, + 2474812119389852814, + 20681229627885468496183596735, + 58699092702719202608937984935, + 2788658612957561183, + 18134285366472732784783506775, + 14093612742815057409863211750, + 668100690380360760, + 65391437020430639731051092678, + 55196548629906002440186932955, + 2732356851226080913, + 75251601695384891581776256264, + 35592685832697107430538005165, + 2300917217142122325, + 57107174132362607981005752004, + 8095041771044196984469602245, + 2410769666991114870, + 15583799840174013454567291967, + 22242311939353116579274144496, + 420958493220869209, + 32505860877071832697315058080, + 23741533053975929589041603073, + 340307804205231046, + 29942134136563091924399781476, + 42035878163258258145045879178, + 719697414780702149, + 71210986978295724024464728603, + 67987573965772207947325937944, + 3151548560356219791, + 78006452256944975093042094244, + 61921666299452277196870693085, + 1729662349469934725, + 6926729674868373918521430672, + 45110366146124627709423288093, + 2948692525280628571, + 40053149999784180433633431454, + 56134263011927012065943581870, + 947246618984267453, + 53100375430660223813496463903, + 24310828419403894711735004981, + 1254251672720303852, + 27508315816690248576740086332, + 48245596698475623073669492014, + 2565685474998509621, + 42220917489538224518668616370, + 23809564713315410717161327408, + 2623795483907320899, + 549363217517117559852988304, + 51321492237007647784080285754, + 3076465454675794191, + 66373957026093268314083483193, + 15541432764592202074612033990, + 2338696161243931359, + 78579309552614338689316606487, + 38944717414514793979068547147, + 1637360973471819930, + 3804038464881616590311298726, + 47209923678200221399667033406, + 1109020328278174176, + 1603827436250855696093174440, + 58185256546490353488143768044, + 1295335391994511726, + 64971256303643774436598185281, + 67355526610966082627565072669, + 1449568364138132790, + 9239714300222262436852971999, + 3930344362586302693499666717, + 3219373323185502993, + 74892934391977427473950233201, + 64897319803808166257741375837, + 3136718833053913139, + 52281833510166044739241356570, + 5084088707775427973907985605, + 2642464394618751731, + 15935278618617491181450505442, + 23220814204280533074457719754, + 3293140429266205417, + 54990319551310402995690092180, + 71756984229400223423483363676, + 1036817513421606581, + 78688290728536847430308578889, + 3478536580013268180787451177, + 147293815444394612, + 51092918974695151216948104029, + 21142025841335865607065767797, + 1347454004974299822, + 37240654008178597794504578513, + 42541197819595709001769524129, + 2772139568220718695, + 63227626464283916717333493662, + 56282667951050639271405003469, + 2130511917009125222, + 5285362100073463319994909379, + 38386263312484164939433119901, + 464119815637709128, + 70840420824631504775489971628, + 23733945776130091578979100808, + 2278475755739036426, + 28203199635894786027039360086, + 50858613978224553191059548660, + 711840399839797704, + 52216974769277018965629890245, + 37948803815377229752117254436, + 1216611319887434485, + 67105096745943321822690029434, + 23842457070786186666660564807, + 199431675580568753, + 59749004853973219855874961327, + 61905058384147930125016972653, + 1311642086645858532, + 11704490222806674528929755545, + 8163419242865430233937922076, + 2353635201809808650, + 20452580501398565824322810447, + 22224612915199693499466055854, + 3398675009296485701, + 77462800490121665895446660708, + 10273155232893062783813966566, + 3411281223007195610, + 3082623324749526374175097405, + 7269922277823384468348212363, + 1151260072359495824, + 7625596887590565896794116020, + 77590985547253993545445657263, + 3442689724868033557, + 15087798314136677487320186846, + 9288962942182604569961447611, + 1377754456115068186, + 45123621128052237919892025137, + 1045694493117493102787169570, + 1721359644953789262, + 66275525763102772178975031880, + 73995508865919031250928724497, + 171253081385893704, + 63341872405564724654006884656, + 5415992620779974917102270513, + 2913802770568837200, + 48536140435606783918887974611, + 16931668582833987690866986150, + 487433587899226301, + 56575203236059140205049282267, + 76516259345892771542291575037, + 2458905618148898142, + 29245754900705208095317437537, + 70665294686017946188779770220, + 3456765640636646467, + 14292125946077362255503757778, + 10961775539650088543157056724, + 1458975568319155203, + 31247328138248707513891550397, + 13655730666713038591624629424, + 571296548357929509, + 46357866708168913384619876794, + 47281700724515679830754600337, + 250438343407399724, + 75007755120970171976925721556, + 44817485956577502349770705521, + 1225915110484157476, + 16293174121228137109997285948, + 344474382532110706528664716, + 1046553341417045229, + 4047372607239200869682097740, + 30639437499055148854843161161, + 1636055494070237335, + 29598346059252802805941112773, + 11287030178002104346744310166, + 943862422368390141, + 70701330040090494696320079802, + 76336988384011901456277250045, + 495335652201117042, + 76609118073901528034537689220, + 60907564515218017117507570261, + 2071235468560586896, + 31906457794252339067731769258, + 35877782403221517898755446727, + 1695470905254114710, + 71700097582076913352098312511, + 29049663534331319396188240183, + 723871068422251791, + 62157927059997347975603835532, + 14076482571379132243016281926, + 3005521309435534877, + 8147270151557989916710005946, + 7635699779740262408670245290, + 1107018600684526505, + 3851291308924411982607962951, + 68575030629627414982667940678, + 475680948229143945, + 45702176636042738793250598242, + 78046668226163809571639984720, + 1350723810028794127, + 62756308417900757632167921227, + 65264972850594401259065972027, + 1791467289499646997, + 44891210900689977657900860667, + 50248918095363177606431801547, + 3465440437920957979, + 7780419647318200451306993713, + 69026419938629101213666220210, + 2645050889677927121, + 61574308755224548589994870465, + 60062802009939189128955294477, + 2835185644784773133, + 68824622003389104758610864483, + 21808687342337156725064202539, + 2388548269785908086, + 71395283977681771825011566477, + 60830111849985076547441423793, + 416837125417290406, + 18122944972960455163819955110, + 50868442343202909216984771953, + 816533666952504014, + 74181485614733881702306863919, + 59964824353858194882759037376, + 2236811176023428367, + 71581310836468716185429510524, + 65551152076379936601151578363, + 2756291345095557505, + 35863061596706371003582877295, + 63968473144965187376724864181, + 3215766166712029189, + 48425701782879890277661035829, + 43266610826584737847671425796, + 924680060579302223, + 76730372192581290204907280806, + 70554869013461040494042925162, + 821721158729468577, + 58647420943392334555066481751, + 29016187566701861506632426903, + 2972711643283478326, + 21563202609499816719911670942, + 24946965656800633340080173505, + 3057263995487630567, + 24968918693188198933172483703, + 49580791577096901056192292585, + 3396284623314214080, + 74496816807583427099702978471, + 6174564428908464210435151834, + 1365699464760568514, + 17479821943643244975092865156, + 3383820028662961358455051359, + 3319182162128748633, + 653040210847186180683568326, + 50615022393324258585740987561, + 2778372882113081500, + 70783794762652411499232649828, + 34385147085888236797900829986, + 543664666826195602, + 52388528745720597474820957957, + 29738526008955738213142479560, + 1793353685264889200, + 25958319850380432793806396015, + 60731436421486203209278001190, + 3069980220818129419, + 23195739835726003889692484855, + 779843289911168631627353870, + 3331809503201568179, + 43968800617441072870666782871, + 19284932106944782822188700174, + 1763099717980589790, + 8303067127489500225659487257, + 10608413506105438948084469278, + 3446909257661569505, + 20589913394790375909048035356, + 78081487069698872701379142980, + 220128892520341502, + 42090168504902890451105352017, + 18753203279513028306460981612, + 1420113445306361455, + 12954249479834833252589674211, + 77779942151057503102385371030, + 428339091911690839, + 10154334592451061067493824619, + 29645278746642286647413846151, + 1657639873042300183, + 75504020971962389056855279280, + 21818437340383733756066770379, + 408607743205712434, + 69484250958978617081912075014, + 14957205616030194095045419266, + 2576492940946496137, + 19169168254028998826522116097, + 3392837399678961516489039872, + 1738765991410178697, + 70078383438790122327827680450, + 11280467881823453862232208921, + 3099104759116607826, + 13920065857208062524194745128, + 34291895902075226855368948797, + 950864274114934932, + 74063409452626517593194070177, + 12895881233701715147417839966, + 1999777236673612593, + 52822790889307931651735270683, + 22549596065537335255801959065, + 207015321620546547, + 23604562531331123615063301556, + 31171184848515011037363087812, + 2726023031386181675, + 37619987384060711979946931731, + 2771468591276147934451506891, + 920566568687058977, + 51501893490061271492347374557, + 35133031623561070301098014936, + 2528166712868318985, + 12688706304792932603664264900, + 43338510036581677007742147577, + 360174571702229388, + 10137843221049369362338805133, + 4084357406062442410014685237, + 892056396290216350, + 20363461217447064960059580380, + 27122812699788953790959745869, + 671555716245914973, + 4293189134835647332007165133, + 38719494890639584022751261953, + 2341116383046215538, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 145, + 11160614802872183630720866117, + 15535587874953487306178388015, + 1842672780645784183, + 1678930071340778499900351929, + 10408518010760180430615264162, + 3250767447857056481, + 32653782586887814055187119004, + 14275511969528782919431033024, + 2175963757551715376, + 32833239315512033466909606842, + 69533650168760881656636351407, + 549728812821474553, + 24459292060035880679997619883, + 54195348889306824481862906503, + 3268752687054176578, + 25830648219429093542685864318, + 67385034518622251823001992132, + 327083856167686887, + 53550671201222684576466721801, + 20968338962173897908540600488, + 2243343667070281714, + 35241484574236882639486760680, + 17789748890893039319822193138, + 1729632706875145336, + 64044459586561117542990185986, + 44762377298498983292781484549, + 534584235474958125, + 46735624579203080242490464626, + 69148706320559245758347593281, + 3014726625179975427, + 56777534788852269406624496577, + 63254461210103234747536516401, + 2934519703609984121, + 3492864391897345323446685238, + 13379841796060538178339074614, + 999498402880536744, + 7051986990770638745260977198, + 7730414169572371846517364090, + 499158952558892071, + 61373576672219239882884211059, + 43710942318336914843425784416, + 1713537075774456096, + 33187073276842948589087513126, + 76372366097102526578210084620, + 944921126451557464, + 55452944998342809811600087364, + 52859513304709822587596740384, + 1124153198902148539, + 74971712394604788823632592018, + 36682383367049838699757702502, + 343113165154133367, + 61183322145783891231396235327, + 41531267521875761541204517445, + 1131211607244822367, + 27989109028021857570459088060, + 28392263563856921233084445308, + 1895917235376592427, + 24353084736031952125582952469, + 14287162842863261071828703981, + 1826369030201020270, + 60699800635267492970307844384, + 62132476785409435659236062911, + 593695600053300369, + 59848076655484720827032446757, + 5394329471710414789014532573, + 1697623745525352602, + 58889104308609891333348953488, + 16463275643025628157612651351, + 1549527501070381451, + 51986789540021201063177346366, + 68430858351982767281446380838, + 2809669658772150718, + 53941597038075476702228108213, + 53087072117400530171207357212, + 3243612762344371725, + 27970290096733089766284155039, + 2375291732450935927990728754, + 1734158911222990915, + 59978038012951178146406759201, + 24484538232468836025473763745, + 2498490352021328596, + 21367193608353106864778983865, + 28263237556062313224434842500, + 717250097698886721, + 42234606379411149145877325416, + 15167976505692589659627927505, + 2673047451621014973, + 21373405733286565329140285471, + 9182126770111993961953560601, + 2134096187857075906, + 58878912373272412659177485675, + 1893720608398183754656128209, + 1798075690237306516, + 60537537097614104241332267654, + 42051454537013134967085888797, + 59146031472052721, + 38899798702225701867287733420, + 64715464267485121176354291206, + 103417505996847429, + 36685978508063247612699503274, + 4403564671571655593316408242, + 2764388233715469615, + 40538798121416246765210152254, + 34234814173569714305218029571, + 1388486505991394119, + 4060697064936834587404897473, + 60674757623514036023870130365, + 2769600351576737761, + 1852827293889366191746440952, + 8353582547152092832016363797, + 2571119805681280451, + 35532366383645276434309278184, + 32736978529480946470591228298, + 687847082537691407, + 7887365245786273311040713936, + 59531081493569389362495343192, + 3289250880389824901, + 32445082970207801624666381706, + 56098182928175683037072086385, + 662437450124287596, + 39558127254330486866150438492, + 66713525558395281103130693098, + 705471244919711377, + 2778700121860478430204252221, + 32005940080304514169804000959, + 978977383131371494, + 26419872143820323822305208467, + 17940775882812205200787187736, + 1031865605328086936, + 72559928054565594123788268800, + 5069128480064718156090777060, + 479761525073602585, + 5782056733283493096200416727, + 18328857653080815188128309668, + 1503406910014438031, + 4069742803456087727122532438, + 50811474173521214854099120627, + 1552298005490314111, + 78538874507404838015335388328, + 62959879527886856838327959525, + 271404259534842760, + 3451687543025188581802506586, + 58241042851933312293668569578, + 2294913320745260104, + 48388301707920994754192054526, + 63240618391742179775812455562, + 3403875943180894295, + 66684080263615341296683208011, + 27340094014785964575751847323, + 3270813256432793483, + 41578643432591730160079737895, + 60468532933947057150876151863, + 1469175234615250765, + 42428148748838565866071876575, + 49710729789930975221769860606, + 3429949918757911160, + 75388426449136202061229803387, + 63199886877224826542996119050, + 3327205446772943258, + 59215935711442958557621030013, + 42593075642630710542821112458, + 1431932696375839130, + 23922550808267285479257743842, + 68404790376904022397531723232, + 219200189752474769, + 41869045440241360455194192221, + 76180453858658606950360816429, + 1835985146811613907, + 39835066207083094488955384661, + 67846105920741099611075084284, + 1099464064806671787, + 16581190028967740994547208760, + 11238458671546824760961658672, + 1443579562648847434, + 4640462958363502081965908503, + 72888280340922014538909090387, + 2338061872780531304, + 37920056839327440430674848137, + 26425718377332368147670882132, + 1459374803979241047, + 71603507478457008272390456018, + 23669804898407692390588669552, + 2128049354327087212, + 59788930875541162375491123452, + 37017760524619293859558499577, + 2995064855903095218, + 68447281913860610491398719053, + 58595085827294341291093928945, + 345900560090762478, + 64241267614085486778562672866, + 15898865683547222091988599156, + 1762724867691990301, + 31220670160659616539431219725, + 42574458288593278376652881193, + 1136654288862193231, + 43926598896492383227691920551, + 59887219173947150671775067432, + 2955716875213878784, + 61591064255048267113346582754, + 11507695820556753240851023445, + 1876644847246187961, + 10950184961326981700930560062, + 7792240114861058682317433157, + 1556082368629120073, + 78962151607847826034655170275, + 60258545885234042321477137367, + 1282351865340555943, + 9675986239227288683957137246, + 8576103849400008781567322117, + 2429108764880148781, + 46991470428767684475632124636, + 52807420746944575157480667710, + 2573848924736194699, + 18983189386294493459954844522, + 6349538642498034521366569951, + 2643823568996706283, + 6790476165166158164342527028, + 65110240856623544414485978590, + 1790901420236350051, + 76428096245901029983367106243, + 43530237165576250165746596809, + 2997856277353217982, + 40443549992106282604241705889, + 1221166547446601204318658322, + 1487753610612848719, + 60496713766843819398744402980, + 45346661574928839959480071326, + 1609038199949157913, + 49385221258086206429261652595, + 25410778867692451037198945236, + 1611552967603068704, + 4310815866942448840562419531, + 36684696676196393493405768140, + 2014885799161593008, + 57545881679566872513463562488, + 62110700367238445578276994592, + 2005104344247353642, + 13603392375929291596252382066, + 1319943321123208562660450647, + 1816981464415142374, + 50450346397141697904186792081, + 33170570964878297999999930044, + 1308709246829668200, + 12015196736042876662988480601, + 49756167737281287118360807779, + 1728831834135639268, + 65654129307355192854887048021, + 6150852485359197928365236937, + 1625544832161360856, + 60827946692815377541801071213, + 45327516463268578905353336250, + 3361692763542040545, + 63876152330254140508127212072, + 15419398473416322892747701159, + 839827806679763605, + 60556920578789588442199832626, + 44126185585849570540257735107, + 348296422526833302, + 11638724238685856200181039786, + 43133412221668922147849397730, + 3077643756485708585, + 7391930562834787153859078857, + 14670263215126684804716921753, + 2216069894940823122, + 71884594686636941188150806125, + 31512732227701676863168672041, + 8419985923772878, + 26099227767702638289513805021, + 12090526653160647371698588362, + 162136317878501097, + 36067754920301300964972018539, + 54398598386783182454774872924, + 2488235182639388217, + 55277041059524813169826964266, + 36386901637213583215830197872, + 1634826994022632480, + 12090268327609234126973508855, + 51291455372964519341434123203, + 1963488548902165204, + 55297403894920387419998882955, + 77215644773813560146054322743, + 1476507367934632575, + 28605613273817068019356198493, + 22164786250341011001122097276, + 2732346517351105097, + 6235572105792039696853964874, + 4390439451645333235875950508, + 970616831894846318, + 22228198969547592539363675971, + 58588512259281530729557139513, + 1401123978661989895, + 29724553809650630712221012861, + 32429866416141163174228559975, + 1237025102601978806, + 7474532193560561637114149162, + 14978685936441334847238157702, + 43870228690925786, + 33389645173477429596645602993, + 17486528084897212424262861062, + 1626516813796252505, + 56896821690266345561180257864, + 24159678869297735167193362198, + 1094250746497187606, + 71888761753692257514974354035, + 75928669203799798904631969237, + 661733470067797059, + 75435365004912095516391188962, + 36294586445353708349492360358, + 1753513962971705077, + 22376022646859576571877433901, + 33828828590783940781003171995, + 471280729924942505, + 63738500982432920386363241168, + 63430637664570471401659202033, + 2677091688916069219, + 61744117772292402509414307451, + 58743887088831447431755913489, + 2572401383812682923, + 4413627442556242420361589445, + 15900528054810873759818704258, + 1772492411698104428, + 58391910250055983848025885719, + 13488698352829986442344435932, + 793893723468537146, + 54675107535307988539388284880, + 61846767296393590987486839420, + 1727313265423854599, + 76325113832651845188548265228, + 3372411597922071491236974585, + 1829246116918799823, + 47642421932583287216970932139, + 19875941148827454057377164317, + 1390415260400619839, + 34278859633295407820347039775, + 4426779903398180720868762768, + 2367367730399350432, + 34291486193580761229332798846, + 56711264349507559027744223966, + 1462146471236787909, + 10671513901484060998284593191, + 67096861143468762609677497827, + 3455892638784249510, + 45913224696986330456272074467, + 10393236963862409675431617617, + 1249276046764384022, + 4849343811249057186445673186, + 79207451130626604213387559900, + 231220963137713015, + 22093780081059671018967616282, + 23243539730316140490499180273, + 633239028185500224, + 64566615412911904495240594245, + 14093994183703483633067771807, + 2412279142792221280, + 78446663571189309530127865554, + 54029349747717614810351328386, + 268233765869157222, + 2735558310250163564266315369, + 66027410850166678715381612475, + 1241627204444484110, + 69890759567053178402099041082, + 74678430483456577506506059867, + 3286441059091308383, + 78885185463837296173991951051, + 30363753458795787757827598803, + 827572939078476613, + 55323567971254502243026268812, + 22421383376039874940361701386, + 2018664774682743561, + 78006437779996186982048467852, + 10088711521846896276656172062, + 3187771633740105911, + 74885423414888926026822863601, + 76875097241570497980196770287, + 586350660221426360, + 24672667992185540246901838526, + 15097121803515946707717403148, + 1057209287302367476, + 52073550657993126672832970616, + 56709874171946514111966755627, + 32286219239983885, + 30829065997986414359064533181, + 73716127453160073789508059676, + 2219820633388612170, + 44705031433282708080072774316, + 15952883471432136858268521841, + 2446472616953001228, + 55183881838822326998610359990, + 43417718625281954131030408134, + 236687184594642281, + 42408421759003435795839037301, + 24200928434576182933787592392, + 3078090679707569950, + 62422481956358489242374068732, + 52321864847754308349258575276, + 3304848686145561425, + 14200702898237708754979810187, + 9935112517674035692193249654, + 534173380190342829, + 23552080953804583261997187826, + 34006949436182870514721855328, + 777076420496484075, + 20459373876977766558015398013, + 9114494938225483169679783713, + 1904218292311338019, + 25233573616679011876325584143, + 6259342690871406348276935992, + 1899254700419984538, + 34282749841436570897356668063, + 22106244781267309313659714956, + 1809297881214917079, + 69243157996795485609973363475, + 21101127135221591032489811805, + 2268817335792383639, + 7604078273011249263142304165, + 37946483173420007914143974687, + 681796980992253260, + 54918840893655773122719257150, + 51609018888502202294831336557, + 1474418088633541060, + 51230088398172587421055943102, + 56229170125702915918063802533, + 2596430704140636329, + 27382679218266502462169530629, + 74245931911347994175489023173, + 3170055294769774784, + 28802903744166279361172693382, + 49559988650671780385212327876, + 2751098919806231102, + 20580077043455014392063588897, + 45453253544485076942877136171, + 362422667621466949, + 16707162827918666184105029474, + 16174998457231736150755593173, + 3251374850055899244, +] + +if __name__ == "__main__": + main() From 676b2bd3cf41849b41b462d18af7d7c7e927de66 Mon Sep 17 00:00:00 2001 From: Rodrigo Ferreira Date: Fri, 17 Jan 2025 12:56:17 -0300 Subject: [PATCH 2/5] Adds honk calldata builder as an isolated python file for guidance --- tests/hydra/test_honk_calldata.py | 1745 +++++++++++++---------------- 1 file changed, 764 insertions(+), 981 deletions(-) diff --git a/tests/hydra/test_honk_calldata.py b/tests/hydra/test_honk_calldata.py index 81573f8c..2407bdcb 100644 --- a/tests/hydra/test_honk_calldata.py +++ b/tests/hydra/test_honk_calldata.py @@ -2,12 +2,11 @@ from abc import ABC, abstractmethod from dataclasses import dataclass, fields from pathlib import Path -from typing import List, Union import sha3 from garaga import garaga_rs -from garaga.algebra import BaseField, PyFelt +from garaga.algebra import PyFelt from garaga.definitions import ( CURVES, STARK, @@ -18,27 +17,6 @@ get_base_field, ) -BATCHED_RELATION_PARTIAL_LENGTH = 8 -CONST_PROOF_SIZE_LOG_N = 28 -G1_PROOF_POINT_SHIFT = 2**136 -G2_POINT_KZG_1 = G2Point.get_nG(CurveID.BN254, 1) -G2_POINT_KZG_2 = G2Point( - x=( - 0x0118C4D5B837BCC2BC89B5B398B5974E9F5944073B32078B7E231FEC938883B0, - 0x260E01B251F6F1C7E7FF4E580791DEE8EA51D87A358E038B4EFE30FAC09383C1, - ), - y=( - 0x22FEBDA3C0C0632A56475B4214E5615E11E6DD3F96E6CEA2854A87D4DACC5E55, - 0x04FC6369F7110FE3D25156C1BB9A72859CF2A04641F99BA4EE413C80DA6A5FE4, - ), - curve_id=CurveID.BN254, -) -MAX_LOG_N = 23 # 2^23 = 8388608 -NUMBER_OF_SUBRELATIONS = 26 -NUMBER_OF_ALPHAS = NUMBER_OF_SUBRELATIONS - 1 -NUMBER_OF_ENTITIES = 44 -NUMBER_UNSHIFTED = 35 - def flatten(t): result = [] @@ -66,7 +44,6 @@ def bigint_split( x = int.from_bytes(x, byteorder="big") else: raise ValueError(f"Invalid type for bigint_split: {type(x)}") - coeffs = [] degree = n_limbs - 1 for n in range(degree, 0, -1): @@ -91,192 +68,6 @@ def bigint_split_array( return xs -@dataclass(slots=True) -class Cairo1SerializableStruct(ABC): - name: str - elmts: list[Union[PyFelt, "Cairo1SerializableStruct"]] - - def __post_init__(self): - assert type(self.name) == str - if isinstance(self.elmts, (list, tuple)): - if len(self.elmts) > 0: - if isinstance(self.elmts[0], Cairo1SerializableStruct): - assert all( - isinstance(elmt, self.elmts[0].__class__) for elmt in self.elmts - ), f"All elements of {self.name} must be of the same type" - - else: - assert all( - isinstance(elmt, PyFelt) for elmt in self.elmts - ), f"All elements of {self.name} must be of type PyFelt, got {type(self.elmts[0])}" - else: - assert self.elmts is None, f"Elmts must be a list or None, got {self.elmts}" - - @property - def struct_name(self) -> str: - return self.__class__.__name__ - - @abstractmethod - def __len__(self) -> int: - pass - - @abstractmethod - def _serialize_to_calldata(self) -> list[int]: - pass - - def serialize_to_calldata(self, *args, **kwargs) -> list[int]: - return self._serialize_to_calldata(*args, **kwargs) - - -class ModuloCircuit: - def __init__( - self, - curve_id: int, - ) -> None: - self.field = BaseField(CURVES[curve_id].p) - self.constants: dict[int, PyFelt] = dict() - self.input_structs: list[Cairo1SerializableStruct] = [] - - def write_element( - self, - elmt: PyFelt | int, - ) -> PyFelt: - if isinstance(elmt, int): - elmt = self.field(elmt) - return elmt - - def write_elements( - self, - elmts: list[PyFelt], - ) -> list[PyFelt]: - return [self.write_element(elmt) for elmt in elmts] - - def write_struct( - self, - struct: Cairo1SerializableStruct, - ) -> Union[ - PyFelt, - List[PyFelt], - List[List[Union[PyFelt, List[PyFelt]]]], - ]: - all_pyfelt = all(type(elmt) == PyFelt for elmt in struct.elmts) - all_cairo1serializablestruct = all( - isinstance(elmt, Cairo1SerializableStruct) for elmt in struct.elmts - ) - assert ( - all_pyfelt or all_cairo1serializablestruct - ), f"Expected list of PyFelt or Cairo1SerializableStruct, got {[type(elmt) for elmt in struct.elmts]}" - - if all_pyfelt: - self.input_structs.append(struct) - if len(struct) == 1 and isinstance(struct, u384): - return self.write_element(struct.elmts[0]) - else: - return self.write_elements(struct.elmts) - elif all_cairo1serializablestruct: - result = [self.write_struct(elmt, write_source) for elmt in struct.elmts] - # Ensure only the larger struct is appended - self.input_structs = [ - s for s in self.input_structs if s not in struct.elmts - ] - self.input_structs.append(struct) - return result - - def mul( - self, - a: PyFelt, - b: PyFelt, - ) -> PyFelt: - if a is None and isinstance(b, PyFelt): - return self.set_or_get_constant(0) - elif b is None and isinstance(a, PyFelt): - return self.set_or_get_constant(0) - assert isinstance(a, PyFelt) and isinstance( - b, PyFelt - ), f"Expected ModuloElement, got lhs {type(a)}, {a} and rhs {type(b)}, {b}" - return self.write_element(a * b) - - def add( - self, - a: PyFelt, - b: PyFelt, - ) -> PyFelt: - if a is None and isinstance(b, PyFelt): - return b - elif b is None and isinstance(a, PyFelt): - return a - else: - assert isinstance(a, PyFelt) and isinstance( - b, PyFelt - ), f"Expected ModuloElement, got {type(a)}, {a} and {type(b)}, {b}" - - return self.write_element(a + b) - - def sub( - self, - a: PyFelt, - b: PyFelt, - ): - return self.write_element(a.felt - b.felt) - - def double(self, a: PyFelt) -> PyFelt: - return self.add(a, a) - - def square(self, a: PyFelt) -> PyFelt: - return self.mul(a, a) - - def neg(self, a: PyFelt) -> PyFelt: - return self.sub(self.set_or_get_constant(self.field.zero()), a) - - def inv( - self, - a: PyFelt, - ): - return self.write_element(a.felt.__inv__()) - - def product(self, args: list[PyFelt]): - if not args: - raise ValueError("The 'args' list cannot be empty.") - assert all(isinstance(elmt, PyFelt) for elmt in args) - result = args[0] - for elmt in args[1:]: - result = self.mul(result, elmt) - return result - - def set_or_get_constant(self, val: PyFelt | int) -> PyFelt: - if isinstance(val, int): - val = self.field(val) - if val.value in self.constants: - return self.constants[val.value] - self.constants[val.value] = self.write_element(val) - return self.constants[val.value] - - -class G1PointCircuit(Cairo1SerializableStruct): - def __init__(self, name: str, elmts: list[PyFelt]): - super().__init__(name, elmts) - self.members_names = ("x", "y") - - @staticmethod - def from_G1Point(name: str, point: G1Point) -> "G1PointCircuit": - field = get_base_field(point.curve_id) - return G1PointCircuit(name=name, elmts=[field(point.x), field(point.y)]) - - @property - def struct_name(self) -> str: - return "G1Point" - - def _serialize_to_calldata(self) -> list[int]: - return bigint_split_array(self.elmts, prepend_length=False) - - def __len__(self) -> int: - if self.elmts is not None: - assert len(self.elmts) == 2 - return 2 - else: - return 2 - - def hades_permutation(s0: int, s1: int, s2: int) -> tuple[int, int, int]: r0, r1, r2 = garaga_rs.hades_permutation( (s0 % STARK).to_bytes(32, "big"), @@ -290,65 +81,112 @@ def hades_permutation(s0: int, s1: int, s2: int) -> tuple[int, int, int]: ) -class Transcript(ABC): - def __init__(self): - self.reset() +def mpc_calldata_builder( + curve_id: CurveID, + pairs: list[G1G2Pair], + n_fixed_g2: int, + public_pair: G1G2Pair | None, +) -> list[int]: + assert isinstance(pairs, list) + assert all(isinstance(pair, G1G2Pair) for pair in pairs) + assert all(curve_id == pair.curve_id == pairs[0].curve_id for pair in pairs) + assert isinstance(public_pair, G1G2Pair) or public_pair is None + assert len(pairs) >= 2 + assert 0 <= n_fixed_g2 <= len(pairs) + return garaga_rs.mpc_calldata_builder( + curve_id.value, + [element.value for pair in pairs for element in pair.to_pyfelt_list()], + n_fixed_g2, + ( + [element.value for element in public_pair.to_pyfelt_list()] + if public_pair is not None + else [] + ), + ) + - @abstractmethod - def reset(self): - pass +def msm_calldata_builder( + points: list[G1Point], + scalars: list[int], + curve_id: CurveID, + include_digits_decomposition=True, + include_points_and_scalars=True, + serialize_as_pure_felt252_array=False, +) -> list[int]: + assert all(point.curve_id == curve_id for point in points) + assert len(points) == len(scalars) + assert all(0 <= s <= CURVES[curve_id.value].n for s in scalars) + calldata = garaga_rs.msm_calldata_builder( + [value for point in points for value in [point.x, point.y]], + scalars, + curve_id.value, + include_digits_decomposition if include_digits_decomposition != None else False, + include_points_and_scalars, + serialize_as_pure_felt252_array, + False, + ) + if include_digits_decomposition == None: + calldata = calldata[1:] + return calldata - @abstractmethod - def update(self, data: bytes): - pass - @abstractmethod - def digest(self) -> bytes: - pass +BATCHED_RELATION_PARTIAL_LENGTH = 8 +CONST_PROOF_SIZE_LOG_N = 28 +G1_PROOF_POINT_SHIFT = 2**136 +NUMBER_OF_SUBRELATIONS = 26 +NUMBER_OF_ALPHAS = NUMBER_OF_SUBRELATIONS - 1 +NUMBER_OF_ENTITIES = 44 +NUMBER_UNSHIFTED = 35 - def digest_reset(self) -> bytes: - res_bytes = self.digest() - self.reset() - return res_bytes +def mul(a: PyFelt, b: PyFelt) -> PyFelt: + assert isinstance(a, PyFelt) + assert isinstance(b, PyFelt) + return a * b -class Sha3Transcript(Transcript): - def reset(self): - self.hasher = sha3.keccak_256() - def digest(self) -> bytes: - res = self.hasher.digest() - res_int = int.from_bytes(res, "big") - res_mod = res_int % CURVES[CurveID.GRUMPKIN.value].p - res_bytes = res_mod.to_bytes(32, "big") - return res_bytes +def add(a: PyFelt, b: PyFelt) -> PyFelt: + assert isinstance(a, PyFelt) + assert isinstance(b, PyFelt) + return a + b - def update(self, data: bytes): - self.hasher.update(data) +def sub(a: PyFelt, b: PyFelt): + assert isinstance(a, PyFelt) + assert isinstance(b, PyFelt) + return a.felt - b.felt -class StarknetPoseidonTranscript(Transcript): - def reset(self): - self.s0, self.s1, self.s2 = hades_permutation( - int.from_bytes(b"StarknetHonk", "big"), 0, 1 - ) - def digest(self) -> bytes: - res_bytes = self.s0.to_bytes(32, "big") - return res_bytes +def double(a: PyFelt) -> PyFelt: + assert isinstance(a, PyFelt) + return a + a - def update(self, data: bytes): - val = int.from_bytes(data, "big") - assert val < 2**256 - high, low = divmod(val, 2**128) - self.s0, self.s1, self.s2 = hades_permutation( - self.s0 + low, self.s1 + high, self.s2 - ) + +def square(a: PyFelt) -> PyFelt: + assert isinstance(a, PyFelt) + return a * a + + +def neg(a: PyFelt) -> PyFelt: + assert isinstance(a, PyFelt) + return -a + + +def inv(a: PyFelt): + assert isinstance(a, PyFelt) + return a.felt.__inv__() + + +def product(args: list[PyFelt]): + assert len(args) > 0 and all(isinstance(elmt, PyFelt) for elmt in args) + result = args[0] + for elmt in args[1:]: + result *= elmt + return result @dataclass class HonkVk: - name: str circuit_size: int log_circuit_size: int public_inputs_size: int @@ -381,59 +219,6 @@ class HonkVk: lagrange_first: G1Point lagrange_last: G1Point - @classmethod - def from_bytes(cls, bytes: bytes) -> "HonkVk": - circuit_size = int.from_bytes(bytes[0:8], "big") - log_circuit_size = int.from_bytes(bytes[8:16], "big") - public_inputs_size = int.from_bytes(bytes[16:24], "big") - public_inputs_offset = int.from_bytes(bytes[24:32], "big") - - cursor = 32 - - rest = bytes[cursor:] - assert len(rest) % 32 == 0 - - # Get all fields that are G1Points from the dataclass - g1_fields = [ - field.name - for field in fields(cls) - if field.type == G1Point and field.name != "name" - ] - - # Parse all G1Points into a dictionary - points = {} - for field_name in g1_fields: - x = int.from_bytes(bytes[cursor : cursor + 32], "big") - y = int.from_bytes(bytes[cursor + 32 : cursor + 64], "big") - points[field_name] = G1Point(x=x, y=y, curve_id=CurveID.BN254) - cursor += 64 - - # Create instance with all parsed values - return cls( - name="", - circuit_size=circuit_size, - log_circuit_size=log_circuit_size, - public_inputs_size=public_inputs_size, - public_inputs_offset=public_inputs_offset, - **points, - ) - - def to_circuit_elements(self, circuit: ModuloCircuit) -> "HonkVk": - return HonkVk( - name=self.name, - circuit_size=self.circuit_size, - log_circuit_size=self.log_circuit_size, - public_inputs_size=self.public_inputs_size, - public_inputs_offset=circuit.write_element(self.public_inputs_offset), - **{ - field.name: circuit.write_struct( - G1PointCircuit.from_G1Point(field.name, getattr(self, field.name)) - ) - for field in fields(self) - if field.type == G1Point and field.name != "name" - }, - ) - @dataclass class HonkProof: @@ -456,10 +241,6 @@ class HonkProof: shplonk_q: G1Point kzg_quotient: G1Point - @property - def log_circuit_size(self) -> int: - return int(math.log2(self.circuit_size)) - def __post_init__(self): assert len(self.sumcheck_univariates) == CONST_PROOF_SIZE_LOG_N assert all( @@ -470,210 +251,6 @@ def __post_init__(self): assert len(self.gemini_fold_comms) == CONST_PROOF_SIZE_LOG_N - 1 assert len(self.gemini_a_evaluations) == CONST_PROOF_SIZE_LOG_N - @classmethod - def from_bytes(cls, bytes: bytes) -> "HonkProof": - n_elements = int.from_bytes(bytes[:4], "big") - assert len(bytes[4:]) % 32 == 0 - elements = [ - int.from_bytes(bytes[i : i + 32], "big") for i in range(4, len(bytes), 32) - ] - assert len(elements) == n_elements - - circuit_size = elements[0] - public_inputs_size = elements[1] - public_inputs_offset = elements[2] - - assert circuit_size <= 2**MAX_LOG_N - - public_inputs = [] - cursor = 3 - for i in range(public_inputs_size): - public_inputs.append(elements[cursor + i]) - - cursor += public_inputs_size - - def parse_g1_proof_point(i: int) -> G1Point: - return G1Point( - x=elements[i] + G1_PROOF_POINT_SHIFT * elements[i + 1], - y=elements[i + 2] + G1_PROOF_POINT_SHIFT * elements[i + 3], - curve_id=CurveID.BN254, - ) - - G1_PROOF_POINT_SIZE = 4 - - w1 = parse_g1_proof_point(cursor) - w2 = parse_g1_proof_point(cursor + G1_PROOF_POINT_SIZE) - w3 = parse_g1_proof_point(cursor + 2 * G1_PROOF_POINT_SIZE) - - lookup_read_counts = parse_g1_proof_point(cursor + 3 * G1_PROOF_POINT_SIZE) - lookup_read_tags = parse_g1_proof_point(cursor + 4 * G1_PROOF_POINT_SIZE) - w4 = parse_g1_proof_point(cursor + 5 * G1_PROOF_POINT_SIZE) - lookup_inverses = parse_g1_proof_point(cursor + 6 * G1_PROOF_POINT_SIZE) - z_perm = parse_g1_proof_point(cursor + 7 * G1_PROOF_POINT_SIZE) - - cursor += 8 * G1_PROOF_POINT_SIZE - - # Parse sumcheck univariates. - sumcheck_univariates = [] - for i in range(CONST_PROOF_SIZE_LOG_N): - sumcheck_univariates.append( - [ - elements[cursor + i * BATCHED_RELATION_PARTIAL_LENGTH + j] - for j in range(BATCHED_RELATION_PARTIAL_LENGTH) - ] - ) - cursor += BATCHED_RELATION_PARTIAL_LENGTH * CONST_PROOF_SIZE_LOG_N - - # Parse sumcheck_evaluations - sumcheck_evaluations = elements[cursor : cursor + NUMBER_OF_ENTITIES] - - cursor += NUMBER_OF_ENTITIES - - # Parse gemini fold comms - gemini_fold_comms = [ - parse_g1_proof_point(cursor + i * G1_PROOF_POINT_SIZE) - for i in range(CONST_PROOF_SIZE_LOG_N - 1) - ] - - cursor += (CONST_PROOF_SIZE_LOG_N - 1) * G1_PROOF_POINT_SIZE - - # Parse gemini a evaluations - gemini_a_evaluations = elements[cursor : cursor + CONST_PROOF_SIZE_LOG_N] - - cursor += CONST_PROOF_SIZE_LOG_N - - shplonk_q = parse_g1_proof_point(cursor) - kzg_quotient = parse_g1_proof_point(cursor + G1_PROOF_POINT_SIZE) - - cursor += 2 * G1_PROOF_POINT_SIZE - - assert cursor == len(elements) - - return HonkProof( - circuit_size=circuit_size, - public_inputs_size=public_inputs_size, - public_inputs_offset=public_inputs_offset, - public_inputs=public_inputs, - w1=w1, - w2=w2, - w3=w3, - w4=w4, - z_perm=z_perm, - lookup_read_counts=lookup_read_counts, - lookup_read_tags=lookup_read_tags, - lookup_inverses=lookup_inverses, - sumcheck_univariates=sumcheck_univariates, - sumcheck_evaluations=sumcheck_evaluations, - gemini_fold_comms=gemini_fold_comms, - gemini_a_evaluations=gemini_a_evaluations, - shplonk_q=shplonk_q, - kzg_quotient=kzg_quotient, - ) - - def to_circuit_elements(self, circuit: ModuloCircuit) -> "HonkProof": - """Convert everything to PyFelts given a circuit.""" - return HonkProof( - circuit_size=self.circuit_size, - public_inputs_size=self.public_inputs_size, - public_inputs_offset=circuit.write_element(self.public_inputs_offset), - public_inputs=circuit.write_elements(self.public_inputs), - w1=circuit.write_struct(G1PointCircuit.from_G1Point("w1", self.w1)), - w2=circuit.write_struct(G1PointCircuit.from_G1Point("w2", self.w2)), - w3=circuit.write_struct(G1PointCircuit.from_G1Point("w3", self.w3)), - w4=circuit.write_struct(G1PointCircuit.from_G1Point("w4", self.w4)), - z_perm=circuit.write_struct( - G1PointCircuit.from_G1Point("z_perm", self.z_perm) - ), - lookup_read_counts=circuit.write_struct( - G1PointCircuit.from_G1Point( - "lookup_read_counts", self.lookup_read_counts - ) - ), - lookup_read_tags=circuit.write_struct( - G1PointCircuit.from_G1Point("lookup_read_tags", self.lookup_read_tags) - ), - lookup_inverses=circuit.write_struct( - G1PointCircuit.from_G1Point("lookup_inverses", self.lookup_inverses) - ), - sumcheck_univariates=[ - circuit.write_elements(univariate) - for univariate in self.sumcheck_univariates - ], - sumcheck_evaluations=circuit.write_elements(self.sumcheck_evaluations), - gemini_fold_comms=[ - circuit.write_struct( - G1PointCircuit.from_G1Point(f"gemini_fold_comm_{i}", comm) - ) - for i, comm in enumerate(self.gemini_fold_comms) - ], - gemini_a_evaluations=circuit.write_elements(self.gemini_a_evaluations), - shplonk_q=circuit.write_struct( - G1PointCircuit.from_G1Point("shplonk_q", self.shplonk_q) - ), - kzg_quotient=circuit.write_struct( - G1PointCircuit.from_G1Point("kzg_quotient", self.kzg_quotient) - ), - ) - - def serialize_to_calldata(self) -> list[int]: - def serialize_G1Point256(g1_point: G1Point) -> list[int]: - xl, xh = split_128(g1_point.x) - yl, yh = split_128(g1_point.y) - return [xl, xh, yl, yh] - - cd = [] - cd.append(self.circuit_size) - cd.append(self.public_inputs_size) - cd.append(self.public_inputs_offset) - cd.extend( - bigint_split_array( - x=self.public_inputs, n_limbs=2, base=2**128, prepend_length=True - ) - ) - cd.extend(serialize_G1Point256(self.w1)) - cd.extend(serialize_G1Point256(self.w2)) - cd.extend(serialize_G1Point256(self.w3)) - cd.extend(serialize_G1Point256(self.w4)) - cd.extend(serialize_G1Point256(self.z_perm)) - cd.extend(serialize_G1Point256(self.lookup_read_counts)) - cd.extend(serialize_G1Point256(self.lookup_read_tags)) - cd.extend(serialize_G1Point256(self.lookup_inverses)) - cd.extend( - bigint_split_array( - x=flatten(self.sumcheck_univariates)[ - : BATCHED_RELATION_PARTIAL_LENGTH * self.log_circuit_size - ], # The rest is 0. - n_limbs=2, - base=2**128, - prepend_length=True, - ) - ) - - cd.extend( - bigint_split_array( - x=self.sumcheck_evaluations, n_limbs=2, base=2**128, prepend_length=True - ) - ) - - cd.append(self.log_circuit_size - 1) - for pt in self.gemini_fold_comms[ - : self.log_circuit_size - 1 - ]: # The rest is G(1, 2) - cd.extend(serialize_G1Point256(pt)) - - cd.extend( - bigint_split_array( - x=self.gemini_a_evaluations[: self.log_circuit_size], - n_limbs=2, - base=2**128, - prepend_length=True, - ) - ) - cd.extend(serialize_G1Point256(self.shplonk_q)) - cd.extend(serialize_G1Point256(self.kzg_quotient)) - - return cd - @dataclass class HonkTranscript: @@ -689,492 +266,480 @@ class HonkTranscript: gemini_r: int | PyFelt shplonk_nu: int | PyFelt shplonk_z: int | PyFelt - public_inputs_delta: int | None = None # Derived. def __post_init__(self): assert len(self.alphas) == NUMBER_OF_ALPHAS assert len(self.gate_challenges) == CONST_PROOF_SIZE_LOG_N assert len(self.sum_check_u_challenges) == CONST_PROOF_SIZE_LOG_N - @classmethod - def from_proof(cls, proof: HonkProof, system="UltraKeccakHonk") -> "HonkTranscript": - def g1_to_g1_proof_point(g1_proof_point: G1Point) -> tuple[int, int, int, int]: - x_high, x_low = divmod(g1_proof_point.x, G1_PROOF_POINT_SHIFT) - y_high, y_low = divmod(g1_proof_point.y, G1_PROOF_POINT_SHIFT) - return (x_low, x_high, y_low, y_high) - - def split_challenge(ch: bytes) -> tuple[int, int]: - ch_int = int.from_bytes(ch, "big") - high_128, low_128 = divmod(ch_int, 2**128) - return (low_128, high_128) - - # Round 0 : circuit_size, public_inputs_size, public_input_offset, [public_inputs], w1, w2, w3 - FR = CURVES[CurveID.GRUMPKIN.value].p - - match system: - case "UltraKeccakHonk": - hasher = Sha3Transcript() - case "UltraStarknetHonk": - hasher = StarknetPoseidonTranscript() - case _: - raise ValueError(f"Proof system {system} not compatible") - - hasher.update(int.to_bytes(proof.circuit_size, 32, "big")) - hasher.update(int.to_bytes(proof.public_inputs_size, 32, "big")) - hasher.update(int.to_bytes(proof.public_inputs_offset, 32, "big")) - - for pub_input in proof.public_inputs: - hasher.update(int.to_bytes(pub_input, 32, "big")) - - for g1_proof_point in [proof.w1, proof.w2, proof.w3]: - # print(f"g1_proof_point: {g1_proof_point.__repr__()}") - x0, x1, y0, y1 = g1_to_g1_proof_point(g1_proof_point) - hasher.update(int.to_bytes(x0, 32, "big")) - hasher.update(int.to_bytes(x1, 32, "big")) - hasher.update(int.to_bytes(y0, 32, "big")) - hasher.update(int.to_bytes(y1, 32, "big")) - - ch0 = hasher.digest_reset() - - eta, eta_two = split_challenge(ch0) - - hasher.update(ch0) - ch0 = hasher.digest_reset() - eta_three, _ = split_challenge(ch0) - - # Round 1 : ch0, lookup_read_counts, lookup_read_tags, w4 - - hasher.update(ch0) - - for g1_proof_point in [ - proof.lookup_read_counts, - proof.lookup_read_tags, - proof.w4, - ]: - x0, x1, y0, y1 = g1_to_g1_proof_point(g1_proof_point) - hasher.update(int.to_bytes(x0, 32, "big")) - hasher.update(int.to_bytes(x1, 32, "big")) - hasher.update(int.to_bytes(y0, 32, "big")) - hasher.update(int.to_bytes(y1, 32, "big")) - - ch1 = hasher.digest_reset() - beta, gamma = split_challenge(ch1) - - # Round 2: ch1, lookup_inverses, z_perm - - hasher.update(ch1) - - for g1_proof_point in [proof.lookup_inverses, proof.z_perm]: - x0, x1, y0, y1 = g1_to_g1_proof_point(g1_proof_point) - hasher.update(int.to_bytes(x0, 32, "big")) - hasher.update(int.to_bytes(x1, 32, "big")) - hasher.update(int.to_bytes(y0, 32, "big")) - hasher.update(int.to_bytes(y1, 32, "big")) - ch2 = hasher.digest_reset() +def serialize_honk_proof_to_calldata(proof: HonkProof) -> list[int]: + def serialize_G1Point256(g1_point: G1Point) -> list[int]: + xl, xh = split_128(g1_point.x) + yl, yh = split_128(g1_point.y) + return [xl, xh, yl, yh] - alphas = [None] * NUMBER_OF_ALPHAS - alphas[0], alphas[1] = split_challenge(ch2) - - for i in range(1, NUMBER_OF_ALPHAS // 2): - hasher.update(ch2) - ch2 = hasher.digest_reset() - alphas[i * 2], alphas[i * 2 + 1] = split_challenge(ch2) - - if NUMBER_OF_ALPHAS % 2 == 1: - hasher.update(ch2) - ch2 = hasher.digest_reset() - alphas[-1], _ = split_challenge(ch2) - - # Round 3: Gate Challenges : - ch3 = ch2 - gate_challenges = [None] * CONST_PROOF_SIZE_LOG_N - for i in range(CONST_PROOF_SIZE_LOG_N): - hasher.update(ch3) - ch3 = hasher.digest_reset() - gate_challenges[i], _ = split_challenge(ch3) - - # Round 4: Sumcheck u challenges - ch4 = ch3 - sum_check_u_challenges = [None] * CONST_PROOF_SIZE_LOG_N - - for i in range(CONST_PROOF_SIZE_LOG_N): - # Create array of univariate challenges starting with previous challenge - univariate_chal = [ch4] - - # Add the sumcheck univariates for this round - for j in range(BATCHED_RELATION_PARTIAL_LENGTH): - univariate_chal.append( - int.to_bytes(proof.sumcheck_univariates[i][j], 32, "big") - ) - - # Update hasher with all univariate challenges - for chal in univariate_chal: - hasher.update(chal) - - # Get next challenge - ch4 = hasher.digest_reset() - - # Split challenge to get sumcheck challenge - sum_check_u_challenges[i], _ = split_challenge(ch4) - - # Rho challenge : - hasher.update(ch4) - for i in range(NUMBER_OF_ENTITIES): - hasher.update(int.to_bytes(proof.sumcheck_evaluations[i], 32, "big")) - - c5 = hasher.digest_reset() - rho, _ = split_challenge(c5) - - # Gemini R : - hasher.update(c5) - for i in range(CONST_PROOF_SIZE_LOG_N - 1): - x0, x1, y0, y1 = g1_to_g1_proof_point(proof.gemini_fold_comms[i]) - hasher.update(int.to_bytes(x0, 32, "big")) - hasher.update(int.to_bytes(x1, 32, "big")) - hasher.update(int.to_bytes(y0, 32, "big")) - hasher.update(int.to_bytes(y1, 32, "big")) - - c6 = hasher.digest_reset() - gemini_r, _ = split_challenge(c6) - - # Shplonk Nu : - hasher.update(c6) - for i in range(CONST_PROOF_SIZE_LOG_N): - hasher.update(int.to_bytes(proof.gemini_a_evaluations[i], 32, "big")) - - c7 = hasher.digest_reset() - shplonk_nu, _ = split_challenge(c7) - - # Shplonk Z : - hasher.update(c7) - x0, x1, y0, y1 = g1_to_g1_proof_point(proof.shplonk_q) - hasher.update(int.to_bytes(x0, 32, "big")) - hasher.update(int.to_bytes(x1, 32, "big")) - hasher.update(int.to_bytes(y0, 32, "big")) - hasher.update(int.to_bytes(y1, 32, "big")) + log_circuit_size = int(math.log2(proof.circuit_size)) - c8 = hasher.digest_reset() - shplonk_z, _ = split_challenge(c8) - - return cls( - eta=eta, - etaTwo=eta_two, - etaThree=eta_three, - beta=beta, - gamma=gamma, - alphas=alphas, - gate_challenges=gate_challenges, - sum_check_u_challenges=sum_check_u_challenges, - rho=rho, - gemini_r=gemini_r, - shplonk_nu=shplonk_nu, - shplonk_z=shplonk_z, - public_inputs_delta=None, + cd = [] + cd.append(proof.circuit_size) + cd.append(proof.public_inputs_size) + cd.append(proof.public_inputs_offset) + cd.extend( + bigint_split_array( + x=proof.public_inputs, n_limbs=2, base=2**128, prepend_length=True ) + ) + cd.extend(serialize_G1Point256(proof.w1)) + cd.extend(serialize_G1Point256(proof.w2)) + cd.extend(serialize_G1Point256(proof.w3)) + cd.extend(serialize_G1Point256(proof.w4)) + cd.extend(serialize_G1Point256(proof.z_perm)) + cd.extend(serialize_G1Point256(proof.lookup_read_counts)) + cd.extend(serialize_G1Point256(proof.lookup_read_tags)) + cd.extend(serialize_G1Point256(proof.lookup_inverses)) + cd.extend( + bigint_split_array( + x=flatten(proof.sumcheck_univariates)[ + : BATCHED_RELATION_PARTIAL_LENGTH * log_circuit_size + ], # The rest is 0. + n_limbs=2, + base=2**128, + prepend_length=True, + ) + ) - def to_circuit_elements(self, circuit: ModuloCircuit) -> "HonkTranscript": - return HonkTranscript( - eta=circuit.write_element(self.eta), - etaTwo=circuit.write_element(self.etaTwo), - etaThree=circuit.write_element(self.etaThree), - beta=circuit.write_element(self.beta), - gamma=circuit.write_element(self.gamma), - alphas=circuit.write_elements(self.alphas), - gate_challenges=circuit.write_elements(self.gate_challenges), - sum_check_u_challenges=circuit.write_elements(self.sum_check_u_challenges), - rho=circuit.write_element(self.rho), - gemini_r=circuit.write_element(self.gemini_r), - shplonk_nu=circuit.write_element(self.shplonk_nu), - shplonk_z=circuit.write_element(self.shplonk_z), - public_inputs_delta=None, + cd.extend( + bigint_split_array( + x=proof.sumcheck_evaluations, n_limbs=2, base=2**128, prepend_length=True ) + ) + cd.append(log_circuit_size - 1) + for pt in proof.gemini_fold_comms[: log_circuit_size - 1]: # The rest is G(1, 2) + cd.extend(serialize_G1Point256(pt)) -class HonkVerifierCircuits(ModuloCircuit): - def __init__( - self, - log_n: int, - curve_id: int = CurveID.GRUMPKIN.value, - ): - super().__init__( - curve_id=curve_id, + cd.extend( + bigint_split_array( + x=proof.gemini_a_evaluations[:log_circuit_size], + n_limbs=2, + base=2**128, + prepend_length=True, ) - self.log_n = log_n - - def compute_shplemini_msm_scalars( - self, - p_sumcheck_evaluations: list[PyFelt], # Full evaluations, not replaced. - p_gemini_a_evaluations: list[PyFelt], - tp_gemini_r: PyFelt, - tp_rho: PyFelt, - tp_shplonk_z: PyFelt, - tp_shplonk_nu: PyFelt, - tp_sumcheck_u_challenges: list[PyFelt], - ) -> list[PyFelt]: - assert all(isinstance(i, PyFelt) for i in p_sumcheck_evaluations) - # function computeSquares(Fr r) internal pure returns (Fr[CONST_PROOF_SIZE_LOG_N] memory squares) { - # squares[0] = r; - # for (uint256 i = 1; i < CONST_PROOF_SIZE_LOG_N; ++i) { - # squares[i] = squares[i - 1].sqr(); - # } - # } - powers_of_evaluations_challenge = [tp_gemini_r] - for i in range(1, self.log_n): - powers_of_evaluations_challenge.append( - self.mul( - powers_of_evaluations_challenge[i - 1], - powers_of_evaluations_challenge[i - 1], - ) - ) + ) + cd.extend(serialize_G1Point256(proof.shplonk_q)) + cd.extend(serialize_G1Point256(proof.kzg_quotient)) - scalars = [self.set_or_get_constant(0)] * ( - NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2 - ) + return cd - # computeInvertedGeminiDenominators - inverse_vanishing_evals = [None] * (CONST_PROOF_SIZE_LOG_N + 1) - inverse_vanishing_evals[0] = self.inv( - self.sub(tp_shplonk_z, powers_of_evaluations_challenge[0]) - ) - for i in range(self.log_n): - inverse_vanishing_evals[i + 1] = self.inv( - self.add(tp_shplonk_z, powers_of_evaluations_challenge[i]) - ) - assert len(inverse_vanishing_evals) == CONST_PROOF_SIZE_LOG_N + 1 +def honk_transcript_from_proof(system: str, proof: HonkProof) -> "HonkTranscript": - # mem.unshiftedScalar = inverse_vanishing_evals[0] + (tp.shplonkNu * inverse_vanishing_evals[1]); - # mem.shiftedScalar = - # tp.geminiR.invert() * (inverse_vanishing_evals[0] - (tp.shplonkNu * inverse_vanishing_evals[1])); + class Transcript(ABC): + def __init__(self): + self.reset() - unshifted_scalar = self.neg( - self.add( - inverse_vanishing_evals[0], - self.mul(tp_shplonk_nu, inverse_vanishing_evals[1]), + @abstractmethod + def reset(self): + pass + + @abstractmethod + def update(self, data: bytes): + pass + + @abstractmethod + def digest(self) -> bytes: + pass + + def digest_reset(self) -> bytes: + res_bytes = self.digest() + self.reset() + return res_bytes + + class Sha3Transcript(Transcript): + def reset(self): + self.hasher = sha3.keccak_256() + + def digest(self) -> bytes: + res = self.hasher.digest() + res_int = int.from_bytes(res, "big") + res_mod = res_int % CURVES[CurveID.GRUMPKIN.value].p + res_bytes = res_mod.to_bytes(32, "big") + return res_bytes + + def update(self, data: bytes): + self.hasher.update(data) + + class StarknetPoseidonTranscript(Transcript): + def reset(self): + self.s0, self.s1, self.s2 = hades_permutation( + int.from_bytes(b"StarknetHonk", "big"), 0, 1 ) - ) - shifted_scalar = self.neg( - self.mul( - self.inv(tp_gemini_r), - self.sub( - inverse_vanishing_evals[0], - self.mul(tp_shplonk_nu, inverse_vanishing_evals[1]), - ), + def digest(self) -> bytes: + return self.s0.to_bytes(32, "big") + + def update(self, data: bytes): + val = int.from_bytes(data, "big") + assert val < 2**256 + high, low = divmod(val, 2**128) + self.s0, self.s1, self.s2 = hades_permutation( + self.s0 + low, self.s1 + high, self.s2 ) - ) - scalars[0] = self.set_or_get_constant(1) + def g1_to_g1_proof_point(g1_proof_point: G1Point) -> tuple[int, int, int, int]: + x_high, x_low = divmod(g1_proof_point.x, G1_PROOF_POINT_SHIFT) + y_high, y_low = divmod(g1_proof_point.y, G1_PROOF_POINT_SHIFT) + return (x_low, x_high, y_low, y_high) + + def split_challenge(ch: bytes) -> tuple[int, int]: + ch_int = int.from_bytes(ch, "big") + high_128, low_128 = divmod(ch_int, 2**128) + return (low_128, high_128) + + # Round 0 : circuit_size, public_inputs_size, public_input_offset, [public_inputs], w1, w2, w3 + + match system: + case "UltraKeccakHonk": + hasher = Sha3Transcript() + case "UltraStarknetHonk": + hasher = StarknetPoseidonTranscript() + case _: + raise ValueError(f"Proof system {system} not compatible") + + hasher.update(int.to_bytes(proof.circuit_size, 32, "big")) + hasher.update(int.to_bytes(proof.public_inputs_size, 32, "big")) + hasher.update(int.to_bytes(proof.public_inputs_offset, 32, "big")) + + for pub_input in proof.public_inputs: + hasher.update(int.to_bytes(pub_input, 32, "big")) + + for g1_proof_point in [proof.w1, proof.w2, proof.w3]: + # print(f"g1_proof_point: {g1_proof_point.__repr__()}") + x0, x1, y0, y1 = g1_to_g1_proof_point(g1_proof_point) + hasher.update(int.to_bytes(x0, 32, "big")) + hasher.update(int.to_bytes(x1, 32, "big")) + hasher.update(int.to_bytes(y0, 32, "big")) + hasher.update(int.to_bytes(y1, 32, "big")) + + ch0 = hasher.digest_reset() + + eta, eta_two = split_challenge(ch0) + + hasher.update(ch0) + ch0 = hasher.digest_reset() + eta_three, _ = split_challenge(ch0) + + # Round 1 : ch0, lookup_read_counts, lookup_read_tags, w4 + + hasher.update(ch0) - batching_challenge = self.set_or_get_constant(1) - batched_evaluation = self.set_or_get_constant(0) + for g1_proof_point in [ + proof.lookup_read_counts, + proof.lookup_read_tags, + proof.w4, + ]: + x0, x1, y0, y1 = g1_to_g1_proof_point(g1_proof_point) + hasher.update(int.to_bytes(x0, 32, "big")) + hasher.update(int.to_bytes(x1, 32, "big")) + hasher.update(int.to_bytes(y0, 32, "big")) + hasher.update(int.to_bytes(y1, 32, "big")) + + ch1 = hasher.digest_reset() + beta, gamma = split_challenge(ch1) + + # Round 2: ch1, lookup_inverses, z_perm + + hasher.update(ch1) - for i in range(1, NUMBER_UNSHIFTED + 1): - scalars[i] = self.mul(unshifted_scalar, batching_challenge) - batched_evaluation = self.add( - batched_evaluation, - self.mul(p_sumcheck_evaluations[i - 1], batching_challenge), + for g1_proof_point in [proof.lookup_inverses, proof.z_perm]: + x0, x1, y0, y1 = g1_to_g1_proof_point(g1_proof_point) + hasher.update(int.to_bytes(x0, 32, "big")) + hasher.update(int.to_bytes(x1, 32, "big")) + hasher.update(int.to_bytes(y0, 32, "big")) + hasher.update(int.to_bytes(y1, 32, "big")) + + ch2 = hasher.digest_reset() + + alphas = [None] * NUMBER_OF_ALPHAS + alphas[0], alphas[1] = split_challenge(ch2) + + for i in range(1, NUMBER_OF_ALPHAS // 2): + hasher.update(ch2) + ch2 = hasher.digest_reset() + alphas[i * 2], alphas[i * 2 + 1] = split_challenge(ch2) + + if NUMBER_OF_ALPHAS % 2 == 1: + hasher.update(ch2) + ch2 = hasher.digest_reset() + alphas[-1], _ = split_challenge(ch2) + + # Round 3: Gate Challenges : + ch3 = ch2 + gate_challenges = [None] * CONST_PROOF_SIZE_LOG_N + for i in range(CONST_PROOF_SIZE_LOG_N): + hasher.update(ch3) + ch3 = hasher.digest_reset() + gate_challenges[i], _ = split_challenge(ch3) + + # Round 4: Sumcheck u challenges + ch4 = ch3 + sum_check_u_challenges = [None] * CONST_PROOF_SIZE_LOG_N + + for i in range(CONST_PROOF_SIZE_LOG_N): + # Create array of univariate challenges starting with previous challenge + univariate_chal = [ch4] + + # Add the sumcheck univariates for this round + for j in range(BATCHED_RELATION_PARTIAL_LENGTH): + univariate_chal.append( + int.to_bytes(proof.sumcheck_univariates[i][j], 32, "big") ) - batching_challenge = self.mul(batching_challenge, tp_rho) - for i in range(NUMBER_UNSHIFTED + 1, NUMBER_OF_ENTITIES + 1): - scalars[i] = self.mul(shifted_scalar, batching_challenge) - batched_evaluation = self.add( - batched_evaluation, - self.mul(p_sumcheck_evaluations[i - 1], batching_challenge), + # Update hasher with all univariate challenges + for chal in univariate_chal: + hasher.update(chal) + + # Get next challenge + ch4 = hasher.digest_reset() + + # Split challenge to get sumcheck challenge + sum_check_u_challenges[i], _ = split_challenge(ch4) + + # Rho challenge : + hasher.update(ch4) + for i in range(NUMBER_OF_ENTITIES): + hasher.update(int.to_bytes(proof.sumcheck_evaluations[i], 32, "big")) + + c5 = hasher.digest_reset() + rho, _ = split_challenge(c5) + + # Gemini R : + hasher.update(c5) + for i in range(CONST_PROOF_SIZE_LOG_N - 1): + x0, x1, y0, y1 = g1_to_g1_proof_point(proof.gemini_fold_comms[i]) + hasher.update(int.to_bytes(x0, 32, "big")) + hasher.update(int.to_bytes(x1, 32, "big")) + hasher.update(int.to_bytes(y0, 32, "big")) + hasher.update(int.to_bytes(y1, 32, "big")) + + c6 = hasher.digest_reset() + gemini_r, _ = split_challenge(c6) + + # Shplonk Nu : + hasher.update(c6) + for i in range(CONST_PROOF_SIZE_LOG_N): + hasher.update(int.to_bytes(proof.gemini_a_evaluations[i], 32, "big")) + + c7 = hasher.digest_reset() + shplonk_nu, _ = split_challenge(c7) + + # Shplonk Z : + hasher.update(c7) + x0, x1, y0, y1 = g1_to_g1_proof_point(proof.shplonk_q) + hasher.update(int.to_bytes(x0, 32, "big")) + hasher.update(int.to_bytes(x1, 32, "big")) + hasher.update(int.to_bytes(y0, 32, "big")) + hasher.update(int.to_bytes(y1, 32, "big")) + + c8 = hasher.digest_reset() + shplonk_z, _ = split_challenge(c8) + + return HonkTranscript( + eta=eta, + etaTwo=eta_two, + etaThree=eta_three, + beta=beta, + gamma=gamma, + alphas=alphas, + gate_challenges=gate_challenges, + sum_check_u_challenges=sum_check_u_challenges, + rho=rho, + gemini_r=gemini_r, + shplonk_nu=shplonk_nu, + shplonk_z=shplonk_z, + ) + + +def circuit_compute_shplemini_msm_scalars( + log_n: int, + p_sumcheck_evaluations: list[PyFelt], # Full evaluations, not replaced. + p_gemini_a_evaluations: list[PyFelt], + tp_gemini_r: PyFelt, + tp_rho: PyFelt, + tp_shplonk_z: PyFelt, + tp_shplonk_nu: PyFelt, + tp_sumcheck_u_challenges: list[PyFelt], +) -> list[PyFelt]: + field = get_base_field(CurveID.GRUMPKIN) + + assert all(isinstance(i, PyFelt) for i in p_sumcheck_evaluations) + powers_of_evaluations_challenge = [tp_gemini_r] + for i in range(1, log_n): + powers_of_evaluations_challenge.append( + mul( + powers_of_evaluations_challenge[i - 1], + powers_of_evaluations_challenge[i - 1], ) - # skip last round: - if i < NUMBER_OF_ENTITIES: - batching_challenge = self.mul(batching_challenge, tp_rho) - - constant_term_accumulator = self.set_or_get_constant(0) - batching_challenge = self.square(tp_shplonk_nu) - - for i in range(CONST_PROOF_SIZE_LOG_N - 1): - dummy_round = i >= (self.log_n - 1) - - scaling_factor = self.set_or_get_constant(0) - if not dummy_round: - scaling_factor = self.mul( - batching_challenge, inverse_vanishing_evals[i + 2] - ) - scalars[NUMBER_OF_ENTITIES + i + 1] = self.neg(scaling_factor) - constant_term_accumulator = self.add( - constant_term_accumulator, - self.mul(scaling_factor, p_gemini_a_evaluations[i + 1]), - ) - else: - # print( - # f"dummy round {i}, index {NUMBER_OF_ENTITIES + i + 1} is set to 0" - # ) - pass - - # skip last round: - if i < self.log_n - 2: - batching_challenge = self.mul(batching_challenge, tp_shplonk_nu) - - # computeGeminiBatchedUnivariateEvaluation - def compute_gemini_batched_univariate_evaluation( - tp_sumcheck_u_challenges, - batched_eval_accumulator, - gemini_evaluations, - gemini_eval_challenge_powers, - ): - for i in range(self.log_n, 0, -1): - challenge_power = gemini_eval_challenge_powers[i - 1] - u = tp_sumcheck_u_challenges[i - 1] - eval_neg = gemini_evaluations[i - 1] - - # (challengePower * batchedEvalAccumulator * Fr.wrap(2)) - evalNeg * (challengePower * (Fr.wrap(1) - u) - u)) - # (challengePower * (Fr.wrap(1) - u) - term = self.mul( - challenge_power, self.sub(self.set_or_get_constant(1), u) - ) - - batched_eval_round_acc = self.sub( - self.double(self.mul(challenge_power, batched_eval_accumulator)), - self.mul(eval_neg, self.sub(term, u)), - ) - - # (challengePower * (Fr.wrap(1) - u) + u).invert() - den = self.add(term, u) - - batched_eval_round_acc = self.mul(batched_eval_round_acc, self.inv(den)) - batched_eval_accumulator = batched_eval_round_acc - - return batched_eval_accumulator - - a_0_pos = compute_gemini_batched_univariate_evaluation( - tp_sumcheck_u_challenges, - batched_evaluation, - p_gemini_a_evaluations, - powers_of_evaluations_challenge, ) - # mem.constantTermAccumulator = mem.constantTermAccumulator + (a_0_pos * inverse_vanishing_evals[0]); - # mem.constantTermAccumulator = - # mem.constantTermAccumulator + (proof.geminiAEvaluations[0] * tp.shplonkNu * inverse_vanishing_evals[1]); + scalars = [field(0)] * (NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2) - constant_term_accumulator = self.add( - constant_term_accumulator, - self.mul(a_0_pos, inverse_vanishing_evals[0]), + # computeInvertedGeminiDenominators + + inverse_vanishing_evals = [None] * (CONST_PROOF_SIZE_LOG_N + 1) + inverse_vanishing_evals[0] = inv( + sub(tp_shplonk_z, powers_of_evaluations_challenge[0]) + ) + for i in range(log_n): + inverse_vanishing_evals[i + 1] = inv( + add(tp_shplonk_z, powers_of_evaluations_challenge[i]) + ) + assert len(inverse_vanishing_evals) == CONST_PROOF_SIZE_LOG_N + 1 + + unshifted_scalar = neg( + add( + inverse_vanishing_evals[0], + mul(tp_shplonk_nu, inverse_vanishing_evals[1]), ) + ) - constant_term_accumulator = self.add( - constant_term_accumulator, - self.product( - [ - p_gemini_a_evaluations[0], - tp_shplonk_nu, - inverse_vanishing_evals[1], - ] + shifted_scalar = neg( + mul( + inv(tp_gemini_r), + sub( + inverse_vanishing_evals[0], + mul(tp_shplonk_nu, inverse_vanishing_evals[1]), ), ) + ) + + scalars[0] = field(1) + + batching_challenge = field(1) + batched_evaluation = field(0) + + for i in range(1, NUMBER_UNSHIFTED + 1): + scalars[i] = mul(unshifted_scalar, batching_challenge) + batched_evaluation = add( + batched_evaluation, + mul(p_sumcheck_evaluations[i - 1], batching_challenge), + ) + batching_challenge = mul(batching_challenge, tp_rho) + + for i in range(NUMBER_UNSHIFTED + 1, NUMBER_OF_ENTITIES + 1): + scalars[i] = mul(shifted_scalar, batching_challenge) + batched_evaluation = add( + batched_evaluation, + mul(p_sumcheck_evaluations[i - 1], batching_challenge), + ) + # skip last round: + if i < NUMBER_OF_ENTITIES: + batching_challenge = mul(batching_challenge, tp_rho) + + constant_term_accumulator = field(0) + batching_challenge = square(tp_shplonk_nu) + + for i in range(CONST_PROOF_SIZE_LOG_N - 1): + dummy_round = i >= (log_n - 1) + + scaling_factor = field(0) + if not dummy_round: + scaling_factor = mul(batching_challenge, inverse_vanishing_evals[i + 2]) + scalars[NUMBER_OF_ENTITIES + i + 1] = neg(scaling_factor) + constant_term_accumulator = add( + constant_term_accumulator, + mul(scaling_factor, p_gemini_a_evaluations[i + 1]), + ) + else: + pass + + # skip last round: + if i < log_n - 2: + batching_challenge = mul(batching_challenge, tp_shplonk_nu) + + # computeGeminiBatchedUnivariateEvaluation + def compute_gemini_batched_univariate_evaluation( + tp_sumcheck_u_challenges, + batched_eval_accumulator, + gemini_evaluations, + gemini_eval_challenge_powers, + ): + for i in range(log_n, 0, -1): + challenge_power = gemini_eval_challenge_powers[i - 1] + u = tp_sumcheck_u_challenges[i - 1] + eval_neg = gemini_evaluations[i - 1] - scalars[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N] = constant_term_accumulator - scalars[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 1] = tp_shplonk_z + term = mul(challenge_power, sub(field(1), u)) - # vk.t1 : 22 + 36 - # vk.t2 : 23 + 37 - # vk.t3 : 24 + 38 - # vk.t4 : 25 + 39 + batched_eval_round_acc = sub( + double(mul(challenge_power, batched_eval_accumulator)), + mul(eval_neg, sub(term, u)), + ) - # proof.w1 : 28 + 40 - # proof.w2 : 29 + 41 - # proof.w3 : 30 + 42 - # proof.w4 : 31 + 43 + den = add(term, u) - scalars[22] = self.add(scalars[22], scalars[36]) - scalars[23] = self.add(scalars[23], scalars[37]) - scalars[24] = self.add(scalars[24], scalars[38]) - scalars[25] = self.add(scalars[25], scalars[39]) + batched_eval_round_acc = mul(batched_eval_round_acc, inv(den)) + batched_eval_accumulator = batched_eval_round_acc - scalars[28] = self.add(scalars[28], scalars[40]) - scalars[29] = self.add(scalars[29], scalars[41]) - scalars[30] = self.add(scalars[30], scalars[42]) - scalars[31] = self.add(scalars[31], scalars[43]) + return batched_eval_accumulator - scalars[36] = None - scalars[37] = None - scalars[38] = None - scalars[39] = None - scalars[40] = None - scalars[41] = None - scalars[42] = None - scalars[43] = None + a_0_pos = compute_gemini_batched_univariate_evaluation( + tp_sumcheck_u_challenges, + batched_evaluation, + p_gemini_a_evaluations, + powers_of_evaluations_challenge, + ) - return scalars + constant_term_accumulator = add( + constant_term_accumulator, + mul(a_0_pos, inverse_vanishing_evals[0]), + ) + constant_term_accumulator = add( + constant_term_accumulator, + product( + [ + p_gemini_a_evaluations[0], + tp_shplonk_nu, + inverse_vanishing_evals[1], + ] + ), + ) -@dataclass(slots=True) -class MPCheckCalldataBuilder: - curve_id: CurveID - pairs: list[G1G2Pair] - n_fixed_g2: int - public_pair: G1G2Pair | None + scalars[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N] = constant_term_accumulator + scalars[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 1] = tp_shplonk_z - def __post_init__(self): - # Validate input - assert isinstance(self.pairs, (list, tuple)) - assert all( - isinstance(pair, G1G2Pair) for pair in self.pairs - ), f"All pairs must be G1G2Pair, got {[type(pair) for pair in self.pairs]}" - assert all( - self.curve_id == pair.curve_id == self.pairs[0].curve_id - for pair in self.pairs - ), f"All pairs must be on the same curve, got {[pair.curve_id for pair in self.pairs]}" - assert ( - isinstance(self.public_pair, G1G2Pair) or self.public_pair is None - ), f"Extra pair must be G1G2Pair or None, got {self.public_pair}" - assert len(self.pairs) >= 2 - assert 0 <= self.n_fixed_g2 <= len(self.pairs) - - def serialize_to_calldata(self) -> list[int]: - return garaga_rs.mpc_calldata_builder( - self.curve_id.value, - [element.value for pair in self.pairs for element in pair.to_pyfelt_list()], - self.n_fixed_g2, - ( - [element.value for element in self.public_pair.to_pyfelt_list()] - if self.public_pair is not None - else [] - ), - ) + # vk.t1 : 22 + 36 + # vk.t2 : 23 + 37 + # vk.t3 : 24 + 38 + # vk.t4 : 25 + 39 + # proof.w1 : 28 + 40 + # proof.w2 : 29 + 41 + # proof.w3 : 30 + 42 + # proof.w4 : 31 + 43 -@dataclass(slots=True) -class MSMCalldataBuilder: - curve_id: CurveID - points: list[G1Point] - scalars: list[int] + scalars[22] = add(scalars[22], scalars[36]) + scalars[23] = add(scalars[23], scalars[37]) + scalars[24] = add(scalars[24], scalars[38]) + scalars[25] = add(scalars[25], scalars[39]) - def __post_init__(self): - assert all( - point.curve_id == self.curve_id for point in self.points - ), "All points must be on the same curve." - assert len(self.points) == len( - self.scalars - ), "Number of points and scalars must be equal." - assert all( - 0 <= s <= CURVES[self.curve_id.value].n for s in self.scalars - ), f"Scalars must be in [0, {self.curve_id.name}'s order] == [0, {CURVES[self.curve_id.value].n}]." + scalars[28] = add(scalars[28], scalars[40]) + scalars[29] = add(scalars[29], scalars[41]) + scalars[30] = add(scalars[30], scalars[42]) + scalars[31] = add(scalars[31], scalars[43]) - def serialize_to_calldata( - self, - include_digits_decomposition=True, - include_points_and_scalars=True, - serialize_as_pure_felt252_array=False, - ) -> list[int]: - return garaga_rs.msm_calldata_builder( - [value for point in self.points for value in [point.x, point.y]], - self.scalars, - self.curve_id.value, - include_digits_decomposition, - include_points_and_scalars, - serialize_as_pure_felt252_array, - False, - ) + scalars[36] = None + scalars[37] = None + scalars[38] = None + scalars[39] = None + scalars[40] = None + scalars[41] = None + scalars[42] = None + scalars[43] = None + + return scalars def extract_msm_scalars(scalars: list[PyFelt], log_n: int) -> list[int]: @@ -1195,15 +760,94 @@ def extract_msm_scalars(scalars: list[PyFelt], log_n: int) -> list[int]: def get_ultra_flavor_honk_calldata_from_vk_and_proof( system: str, vk: HonkVk, proof: HonkProof ) -> list[int]: - tp = HonkTranscript.from_proof(proof, system) + tp = honk_transcript_from_proof(system, proof) + + def circuit_write_element(elmt: PyFelt | int) -> PyFelt: + field = get_base_field(CurveID.GRUMPKIN.value) + return field(elmt) if isinstance(elmt, int) else elmt + + def circuit_write_elements(elmts: list[PyFelt]) -> list[PyFelt]: + return [circuit_write_element(elmt) for elmt in elmts] + + def from_G1Point(point: G1Point) -> list[PyFelt]: + field = get_base_field(point.curve_id) + return [field(point.x), field(point.y)] + + vk_circuit = HonkVk( + circuit_size=vk.circuit_size, + log_circuit_size=vk.log_circuit_size, + public_inputs_size=vk.public_inputs_size, + public_inputs_offset=circuit_write_element(vk.public_inputs_offset), + qm=from_G1Point(vk.qm), + qc=from_G1Point(vk.qc), + ql=from_G1Point(vk.ql), + qr=from_G1Point(vk.qr), + qo=from_G1Point(vk.qo), + q4=from_G1Point(vk.q4), + qArith=from_G1Point(vk.qArith), + qDeltaRange=from_G1Point(vk.qDeltaRange), + qElliptic=from_G1Point(vk.qElliptic), + qAux=from_G1Point(vk.qAux), + qLookup=from_G1Point(vk.qLookup), + qPoseidon2External=from_G1Point(vk.qPoseidon2External), + qPoseidon2Internal=from_G1Point(vk.qPoseidon2Internal), + s1=from_G1Point(vk.s1), + s2=from_G1Point(vk.s2), + s3=from_G1Point(vk.s3), + s4=from_G1Point(vk.s4), + id1=from_G1Point(vk.id1), + id2=from_G1Point(vk.id2), + id3=from_G1Point(vk.id3), + id4=from_G1Point(vk.id4), + t1=from_G1Point(vk.t1), + t2=from_G1Point(vk.t2), + t3=from_G1Point(vk.t3), + t4=from_G1Point(vk.t4), + lagrange_first=from_G1Point(vk.lagrange_first), + lagrange_last=from_G1Point(vk.lagrange_last), + ) - circuit = HonkVerifierCircuits(log_n=vk.log_circuit_size) + proof_circuit = HonkProof( + circuit_size=proof.circuit_size, + public_inputs_size=proof.public_inputs_size, + public_inputs_offset=circuit_write_element(proof.public_inputs_offset), + public_inputs=circuit_write_elements(proof.public_inputs), + w1=from_G1Point(proof.w1), + w2=from_G1Point(proof.w2), + w3=from_G1Point(proof.w3), + w4=from_G1Point(proof.w4), + z_perm=from_G1Point(proof.z_perm), + lookup_read_counts=from_G1Point(proof.lookup_read_counts), + lookup_read_tags=from_G1Point(proof.lookup_read_tags), + lookup_inverses=from_G1Point(proof.lookup_inverses), + sumcheck_univariates=[ + circuit_write_elements(univariate) + for univariate in proof.sumcheck_univariates + ], + sumcheck_evaluations=circuit_write_elements(proof.sumcheck_evaluations), + gemini_fold_comms=[from_G1Point(comm) for comm in proof.gemini_fold_comms], + gemini_a_evaluations=circuit_write_elements(proof.gemini_a_evaluations), + shplonk_q=from_G1Point(proof.shplonk_q), + kzg_quotient=from_G1Point(proof.kzg_quotient), + ) - vk_circuit = vk.to_circuit_elements(circuit) - proof_circuit = proof.to_circuit_elements(circuit) - tp = tp.to_circuit_elements(circuit) + tp = HonkTranscript( + eta=circuit_write_element(tp.eta), + etaTwo=circuit_write_element(tp.etaTwo), + etaThree=circuit_write_element(tp.etaThree), + beta=circuit_write_element(tp.beta), + gamma=circuit_write_element(tp.gamma), + alphas=circuit_write_elements(tp.alphas), + gate_challenges=circuit_write_elements(tp.gate_challenges), + sum_check_u_challenges=circuit_write_elements(tp.sum_check_u_challenges), + rho=circuit_write_element(tp.rho), + gemini_r=circuit_write_element(tp.gemini_r), + shplonk_nu=circuit_write_element(tp.shplonk_nu), + shplonk_z=circuit_write_element(tp.shplonk_z), + ) - scalars = circuit.compute_shplemini_msm_scalars( + scalars = circuit_compute_shplemini_msm_scalars( + vk.log_circuit_size, proof_circuit.sumcheck_evaluations, proof_circuit.gemini_a_evaluations, tp.gemini_r, @@ -1257,39 +901,184 @@ def get_ultra_flavor_honk_calldata_from_vk_and_proof( points.append(G1Point.get_nG(CurveID.BN254, 1)) points.append(proof.kzg_quotient) - msm_builder = MSMCalldataBuilder(CurveID.BN254, points=points, scalars=scalars_msm) + msm_data = msm_calldata_builder( + points, + scalars_msm, + CurveID.BN254, + include_digits_decomposition=None, + include_points_and_scalars=False, + serialize_as_pure_felt252_array=False, + ) + + G2_POINT_KZG_1 = G2Point.get_nG(CurveID.BN254, 1) + G2_POINT_KZG_2 = G2Point( + x=( + 0x0118C4D5B837BCC2BC89B5B398B5974E9F5944073B32078B7E231FEC938883B0, + 0x260E01B251F6F1C7E7FF4E580791DEE8EA51D87A358E038B4EFE30FAC09383C1, + ), + y=( + 0x22FEBDA3C0C0632A56475B4214E5615E11E6DD3F96E6CEA2854A87D4DACC5E55, + 0x04FC6369F7110FE3D25156C1BB9A72859CF2A04641F99BA4EE413C80DA6A5FE4, + ), + curve_id=CurveID.BN254, + ) P_0 = G1Point.msm(points=points, scalars=scalars_msm).add(proof.shplonk_q) P_1 = -proof.kzg_quotient pairs = [G1G2Pair(P_0, G2_POINT_KZG_1), G1G2Pair(P_1, G2_POINT_KZG_2)] - mpc_builder = MPCheckCalldataBuilder( - curve_id=CurveID.BN254, pairs=pairs, n_fixed_g2=2, public_pair=None + mpc_data = mpc_calldata_builder( + curve_id=CurveID.BN254, + pairs=pairs, + n_fixed_g2=2, + public_pair=None, ) + cd = [] - cd.extend(proof.serialize_to_calldata()) - cd.extend( - msm_builder.serialize_to_calldata( - include_points_and_scalars=False, - serialize_as_pure_felt252_array=False, - include_digits_decomposition=False, - ) + cd.extend(serialize_honk_proof_to_calldata(proof)) + cd.extend(msm_data) + cd.extend(mpc_data) + return [len(cd)] + cd + + +def get_honk_calldata(system: str, vk: Path, proof: Path) -> list[int]: + vk_obj = honk_vk_from_bytes(open(vk, "rb").read()) + proof_obj = honk_proof_from_bytes(open(proof, "rb").read()) + return get_ultra_flavor_honk_calldata_from_vk_and_proof(system, vk_obj, proof_obj) + + +def honk_vk_from_bytes(bytes: bytes) -> HonkVk: + circuit_size = int.from_bytes(bytes[0:8], "big") + log_circuit_size = int.from_bytes(bytes[8:16], "big") + public_inputs_size = int.from_bytes(bytes[16:24], "big") + public_inputs_offset = int.from_bytes(bytes[24:32], "big") + + cursor = 32 + + rest = bytes[cursor:] + assert len(rest) % 32 == 0 + + # Get all fields that are G1Points from the dataclass + g1_fields = [field.name for field in fields(HonkVk) if field.type == G1Point] + + # Parse all G1Points into a dictionary + points = {} + for field_name in g1_fields: + x = int.from_bytes(bytes[cursor : cursor + 32], "big") + y = int.from_bytes(bytes[cursor + 32 : cursor + 64], "big") + points[field_name] = G1Point(x=x, y=y, curve_id=CurveID.BN254) + cursor += 64 + + # Create instance with all parsed values + return HonkVk( + circuit_size=circuit_size, + log_circuit_size=log_circuit_size, + public_inputs_size=public_inputs_size, + public_inputs_offset=public_inputs_offset, + **points, ) - cd.extend(mpc_builder.serialize_to_calldata()) - res = [len(cd)] + cd - # print(f"HONK CALLDATA: {res}") - # print(f"HONK CALLDATA LENGTH: {len(res)}") +def honk_proof_from_bytes(bytes: bytes) -> HonkProof: + n_elements = int.from_bytes(bytes[:4], "big") + assert len(bytes[4:]) % 32 == 0 + elements = [ + int.from_bytes(bytes[i : i + 32], "big") for i in range(4, len(bytes), 32) + ] + assert len(elements) == n_elements - return res + circuit_size = elements[0] + public_inputs_size = elements[1] + public_inputs_offset = elements[2] + MAX_LOG_N = 23 # 2^23 = 8388608 + assert circuit_size <= 2**MAX_LOG_N -def get_honk_calldata(system: str, vk: Path, proof: Path) -> list[int]: - vk_obj = HonkVk.from_bytes(open(vk, "rb").read()) - proof_obj = HonkProof.from_bytes(open(proof, "rb").read()) - return get_ultra_flavor_honk_calldata_from_vk_and_proof(system, vk_obj, proof_obj) + public_inputs = [] + cursor = 3 + for i in range(public_inputs_size): + public_inputs.append(elements[cursor + i]) + + cursor += public_inputs_size + + def parse_g1_proof_point(i: int) -> G1Point: + return G1Point( + x=elements[i] + G1_PROOF_POINT_SHIFT * elements[i + 1], + y=elements[i + 2] + G1_PROOF_POINT_SHIFT * elements[i + 3], + curve_id=CurveID.BN254, + ) + + G1_PROOF_POINT_SIZE = 4 + + w1 = parse_g1_proof_point(cursor) + w2 = parse_g1_proof_point(cursor + G1_PROOF_POINT_SIZE) + w3 = parse_g1_proof_point(cursor + 2 * G1_PROOF_POINT_SIZE) + + lookup_read_counts = parse_g1_proof_point(cursor + 3 * G1_PROOF_POINT_SIZE) + lookup_read_tags = parse_g1_proof_point(cursor + 4 * G1_PROOF_POINT_SIZE) + w4 = parse_g1_proof_point(cursor + 5 * G1_PROOF_POINT_SIZE) + lookup_inverses = parse_g1_proof_point(cursor + 6 * G1_PROOF_POINT_SIZE) + z_perm = parse_g1_proof_point(cursor + 7 * G1_PROOF_POINT_SIZE) + + cursor += 8 * G1_PROOF_POINT_SIZE + + # Parse sumcheck univariates. + sumcheck_univariates = [] + for i in range(CONST_PROOF_SIZE_LOG_N): + sumcheck_univariates.append( + [ + elements[cursor + i * BATCHED_RELATION_PARTIAL_LENGTH + j] + for j in range(BATCHED_RELATION_PARTIAL_LENGTH) + ] + ) + cursor += BATCHED_RELATION_PARTIAL_LENGTH * CONST_PROOF_SIZE_LOG_N + + # Parse sumcheck_evaluations + sumcheck_evaluations = elements[cursor : cursor + NUMBER_OF_ENTITIES] + + cursor += NUMBER_OF_ENTITIES + + # Parse gemini fold comms + gemini_fold_comms = [ + parse_g1_proof_point(cursor + i * G1_PROOF_POINT_SIZE) + for i in range(CONST_PROOF_SIZE_LOG_N - 1) + ] + + cursor += (CONST_PROOF_SIZE_LOG_N - 1) * G1_PROOF_POINT_SIZE + + # Parse gemini a evaluations + gemini_a_evaluations = elements[cursor : cursor + CONST_PROOF_SIZE_LOG_N] + + cursor += CONST_PROOF_SIZE_LOG_N + + shplonk_q = parse_g1_proof_point(cursor) + kzg_quotient = parse_g1_proof_point(cursor + G1_PROOF_POINT_SIZE) + + cursor += 2 * G1_PROOF_POINT_SIZE + + assert cursor == len(elements) + + return HonkProof( + circuit_size=circuit_size, + public_inputs_size=public_inputs_size, + public_inputs_offset=public_inputs_offset, + public_inputs=public_inputs, + w1=w1, + w2=w2, + w3=w3, + w4=w4, + z_perm=z_perm, + lookup_read_counts=lookup_read_counts, + lookup_read_tags=lookup_read_tags, + lookup_inverses=lookup_inverses, + sumcheck_univariates=sumcheck_univariates, + sumcheck_evaluations=sumcheck_evaluations, + gemini_fold_comms=gemini_fold_comms, + gemini_a_evaluations=gemini_a_evaluations, + shplonk_q=shplonk_q, + kzg_quotient=kzg_quotient, + ) def main(): @@ -1306,17 +1095,11 @@ def main(): vk = examples_folder_path / "vk_ultra_keccak.bin" proof = examples_folder_path / "proof_ultra_keccak.bin" calldata = get_honk_calldata("UltraKeccakHonk", vk, proof) - # fix for garaga_rs.msm - calldata = calldata[:245] + calldata[246:] - calldata[0] -= 1 assert calldata == ULTRA_KECCAK_CALLDATA vk = examples_folder_path / "vk_ultra_keccak.bin" proof = examples_folder_path / "proof_ultra_starknet.bin" calldata = get_honk_calldata("UltraStarknetHonk", vk, proof) - # fix for garaga_rs.msm - calldata = calldata[:245] + calldata[246:] - calldata[0] -= 1 assert calldata == ULTRA_STARKNET_CALLDATA print("success") From 021079aa0bef2dfbb3a79d184b3b75643b9b221a Mon Sep 17 00:00:00 2001 From: Rodrigo Ferreira Date: Fri, 17 Jan 2025 16:15:04 -0300 Subject: [PATCH 3/5] More adjustments to test_honk_calldata.py to simplify translation --- tests/hydra/test_honk_calldata.py | 379 ++++++++++-------------------- 1 file changed, 125 insertions(+), 254 deletions(-) diff --git a/tests/hydra/test_honk_calldata.py b/tests/hydra/test_honk_calldata.py index 2407bdcb..fb916070 100644 --- a/tests/hydra/test_honk_calldata.py +++ b/tests/hydra/test_honk_calldata.py @@ -1,6 +1,6 @@ import math from abc import ABC, abstractmethod -from dataclasses import dataclass, fields +from dataclasses import dataclass from pathlib import Path import sha3 @@ -139,52 +139,6 @@ def msm_calldata_builder( NUMBER_UNSHIFTED = 35 -def mul(a: PyFelt, b: PyFelt) -> PyFelt: - assert isinstance(a, PyFelt) - assert isinstance(b, PyFelt) - return a * b - - -def add(a: PyFelt, b: PyFelt) -> PyFelt: - assert isinstance(a, PyFelt) - assert isinstance(b, PyFelt) - return a + b - - -def sub(a: PyFelt, b: PyFelt): - assert isinstance(a, PyFelt) - assert isinstance(b, PyFelt) - return a.felt - b.felt - - -def double(a: PyFelt) -> PyFelt: - assert isinstance(a, PyFelt) - return a + a - - -def square(a: PyFelt) -> PyFelt: - assert isinstance(a, PyFelt) - return a * a - - -def neg(a: PyFelt) -> PyFelt: - assert isinstance(a, PyFelt) - return -a - - -def inv(a: PyFelt): - assert isinstance(a, PyFelt) - return a.felt.__inv__() - - -def product(args: list[PyFelt]): - assert len(args) > 0 and all(isinstance(elmt, PyFelt) for elmt in args) - result = args[0] - for elmt in args[1:]: - result *= elmt - return result - - @dataclass class HonkVk: circuit_size: int @@ -254,18 +208,18 @@ def __post_init__(self): @dataclass class HonkTranscript: - eta: int | PyFelt - etaTwo: int | PyFelt - etaThree: int | PyFelt - beta: int | PyFelt - gamma: int | PyFelt - alphas: list[int | PyFelt] - gate_challenges: list[int | PyFelt] + eta: PyFelt + etaTwo: PyFelt + etaThree: PyFelt + beta: PyFelt + gamma: PyFelt + alphas: list[PyFelt] + gate_challenges: list[PyFelt] sum_check_u_challenges: list[PyFelt] - rho: int | PyFelt - gemini_r: int | PyFelt - shplonk_nu: int | PyFelt - shplonk_z: int | PyFelt + rho: PyFelt + gemini_r: PyFelt + shplonk_nu: PyFelt + shplonk_z: PyFelt def __post_init__(self): assert len(self.alphas) == NUMBER_OF_ALPHAS @@ -333,9 +287,9 @@ def serialize_G1Point256(g1_point: G1Point) -> list[int]: return cd -def honk_transcript_from_proof(system: str, proof: HonkProof) -> "HonkTranscript": +def honk_transcript_from_proof(system: str, proof: HonkProof) -> HonkTranscript: - class Transcript(ABC): + class Hasher(ABC): def __init__(self): self.reset() @@ -356,7 +310,7 @@ def digest_reset(self) -> bytes: self.reset() return res_bytes - class Sha3Transcript(Transcript): + class KeccakHasher(Hasher): def reset(self): self.hasher = sha3.keccak_256() @@ -370,7 +324,7 @@ def digest(self) -> bytes: def update(self, data: bytes): self.hasher.update(data) - class StarknetPoseidonTranscript(Transcript): + class StarknetHasher(Hasher): def reset(self): self.s0, self.s1, self.s2 = hades_permutation( int.from_bytes(b"StarknetHonk", "big"), 0, 1 @@ -401,9 +355,9 @@ def split_challenge(ch: bytes) -> tuple[int, int]: match system: case "UltraKeccakHonk": - hasher = Sha3Transcript() + hasher = KeccakHasher() case "UltraStarknetHonk": - hasher = StarknetPoseidonTranscript() + hasher = StarknetHasher() case _: raise ValueError(f"Proof system {system} not compatible") @@ -415,7 +369,6 @@ def split_challenge(ch: bytes) -> tuple[int, int]: hasher.update(int.to_bytes(pub_input, 32, "big")) for g1_proof_point in [proof.w1, proof.w2, proof.w3]: - # print(f"g1_proof_point: {g1_proof_point.__repr__()}") x0, x1, y0, y1 = g1_to_g1_proof_point(g1_proof_point) hasher.update(int.to_bytes(x0, 32, "big")) hasher.update(int.to_bytes(x1, 32, "big")) @@ -545,19 +498,23 @@ def split_challenge(ch: bytes) -> tuple[int, int]: c8 = hasher.digest_reset() shplonk_z, _ = split_challenge(c8) + field = get_base_field(CurveID.GRUMPKIN) return HonkTranscript( - eta=eta, - etaTwo=eta_two, - etaThree=eta_three, - beta=beta, - gamma=gamma, - alphas=alphas, - gate_challenges=gate_challenges, - sum_check_u_challenges=sum_check_u_challenges, - rho=rho, - gemini_r=gemini_r, - shplonk_nu=shplonk_nu, - shplonk_z=shplonk_z, + eta=field(eta), + etaTwo=field(eta_two), + etaThree=field(eta_three), + beta=field(beta), + gamma=field(gamma), + alphas=[field(alpha) for alpha in alphas], + gate_challenges=[field(gate_challenge) for gate_challenge in gate_challenges], + sum_check_u_challenges=[ + field(sum_check_u_challenge) + for sum_check_u_challenge in sum_check_u_challenges + ], + rho=field(rho), + gemini_r=field(gemini_r), + shplonk_nu=field(shplonk_nu), + shplonk_z=field(shplonk_z), ) @@ -573,14 +530,20 @@ def circuit_compute_shplemini_msm_scalars( ) -> list[PyFelt]: field = get_base_field(CurveID.GRUMPKIN) + assert all(elem.p == field.p for elem in p_sumcheck_evaluations) + assert all(elem.p == field.p for elem in p_gemini_a_evaluations) + assert tp_gemini_r.p == field.p + assert tp_rho.p == field.p + assert tp_shplonk_z.p == field.p + assert tp_shplonk_nu.p == field.p + assert all(elem.p == field.p for elem in tp_sumcheck_u_challenges) + assert all(isinstance(i, PyFelt) for i in p_sumcheck_evaluations) powers_of_evaluations_challenge = [tp_gemini_r] for i in range(1, log_n): powers_of_evaluations_challenge.append( - mul( - powers_of_evaluations_challenge[i - 1], - powers_of_evaluations_challenge[i - 1], - ) + powers_of_evaluations_challenge[i - 1] + * powers_of_evaluations_challenge[i - 1] ) scalars = [field(0)] * (NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2) @@ -588,30 +551,22 @@ def circuit_compute_shplemini_msm_scalars( # computeInvertedGeminiDenominators inverse_vanishing_evals = [None] * (CONST_PROOF_SIZE_LOG_N + 1) - inverse_vanishing_evals[0] = inv( - sub(tp_shplonk_z, powers_of_evaluations_challenge[0]) - ) + inverse_vanishing_evals[0] = ( + tp_shplonk_z - powers_of_evaluations_challenge[0] + ).__inv__() for i in range(log_n): - inverse_vanishing_evals[i + 1] = inv( - add(tp_shplonk_z, powers_of_evaluations_challenge[i]) - ) + inverse_vanishing_evals[i + 1] = ( + tp_shplonk_z + powers_of_evaluations_challenge[i] + ).__inv__() assert len(inverse_vanishing_evals) == CONST_PROOF_SIZE_LOG_N + 1 - unshifted_scalar = neg( - add( - inverse_vanishing_evals[0], - mul(tp_shplonk_nu, inverse_vanishing_evals[1]), - ) + unshifted_scalar = -( + inverse_vanishing_evals[0] + tp_shplonk_nu * inverse_vanishing_evals[1] ) - shifted_scalar = neg( - mul( - inv(tp_gemini_r), - sub( - inverse_vanishing_evals[0], - mul(tp_shplonk_nu, inverse_vanishing_evals[1]), - ), - ) + shifted_scalar = -( + tp_gemini_r.__inv__() + * (inverse_vanishing_evals[0] - tp_shplonk_nu * inverse_vanishing_evals[1]) ) scalars[0] = field(1) @@ -620,43 +575,39 @@ def circuit_compute_shplemini_msm_scalars( batched_evaluation = field(0) for i in range(1, NUMBER_UNSHIFTED + 1): - scalars[i] = mul(unshifted_scalar, batching_challenge) - batched_evaluation = add( - batched_evaluation, - mul(p_sumcheck_evaluations[i - 1], batching_challenge), + scalars[i] = unshifted_scalar * batching_challenge + batched_evaluation = ( + batched_evaluation + p_sumcheck_evaluations[i - 1] * batching_challenge ) - batching_challenge = mul(batching_challenge, tp_rho) + batching_challenge = batching_challenge * tp_rho for i in range(NUMBER_UNSHIFTED + 1, NUMBER_OF_ENTITIES + 1): - scalars[i] = mul(shifted_scalar, batching_challenge) - batched_evaluation = add( - batched_evaluation, - mul(p_sumcheck_evaluations[i - 1], batching_challenge), + scalars[i] = shifted_scalar * batching_challenge + batched_evaluation = ( + batched_evaluation + p_sumcheck_evaluations[i - 1] * batching_challenge ) # skip last round: if i < NUMBER_OF_ENTITIES: - batching_challenge = mul(batching_challenge, tp_rho) + batching_challenge = batching_challenge * tp_rho constant_term_accumulator = field(0) - batching_challenge = square(tp_shplonk_nu) + batching_challenge = tp_shplonk_nu * tp_shplonk_nu for i in range(CONST_PROOF_SIZE_LOG_N - 1): dummy_round = i >= (log_n - 1) scaling_factor = field(0) if not dummy_round: - scaling_factor = mul(batching_challenge, inverse_vanishing_evals[i + 2]) - scalars[NUMBER_OF_ENTITIES + i + 1] = neg(scaling_factor) - constant_term_accumulator = add( - constant_term_accumulator, - mul(scaling_factor, p_gemini_a_evaluations[i + 1]), + scaling_factor = batching_challenge * inverse_vanishing_evals[i + 2] + scalars[NUMBER_OF_ENTITIES + i + 1] = -scaling_factor + constant_term_accumulator = ( + constant_term_accumulator + + scaling_factor * p_gemini_a_evaluations[i + 1] ) - else: - pass # skip last round: if i < log_n - 2: - batching_challenge = mul(batching_challenge, tp_shplonk_nu) + batching_challenge = batching_challenge * tp_shplonk_nu # computeGeminiBatchedUnivariateEvaluation def compute_gemini_batched_univariate_evaluation( @@ -670,16 +621,15 @@ def compute_gemini_batched_univariate_evaluation( u = tp_sumcheck_u_challenges[i - 1] eval_neg = gemini_evaluations[i - 1] - term = mul(challenge_power, sub(field(1), u)) + term = challenge_power * (field(1) - u) - batched_eval_round_acc = sub( - double(mul(challenge_power, batched_eval_accumulator)), - mul(eval_neg, sub(term, u)), - ) + batched_eval_round_acc = ( + field(2) * challenge_power * batched_eval_accumulator + ) - (eval_neg * (term - u)) - den = add(term, u) + den = term + u - batched_eval_round_acc = mul(batched_eval_round_acc, inv(den)) + batched_eval_round_acc = batched_eval_round_acc * den.__inv__() batched_eval_accumulator = batched_eval_round_acc return batched_eval_accumulator @@ -691,20 +641,13 @@ def compute_gemini_batched_univariate_evaluation( powers_of_evaluations_challenge, ) - constant_term_accumulator = add( - constant_term_accumulator, - mul(a_0_pos, inverse_vanishing_evals[0]), + constant_term_accumulator = ( + constant_term_accumulator + a_0_pos * inverse_vanishing_evals[0] ) - constant_term_accumulator = add( - constant_term_accumulator, - product( - [ - p_gemini_a_evaluations[0], - tp_shplonk_nu, - inverse_vanishing_evals[1], - ] - ), + constant_term_accumulator = ( + constant_term_accumulator + + p_gemini_a_evaluations[0] * tp_shplonk_nu * inverse_vanishing_evals[1] ) scalars[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N] = constant_term_accumulator @@ -720,15 +663,15 @@ def compute_gemini_batched_univariate_evaluation( # proof.w3 : 30 + 42 # proof.w4 : 31 + 43 - scalars[22] = add(scalars[22], scalars[36]) - scalars[23] = add(scalars[23], scalars[37]) - scalars[24] = add(scalars[24], scalars[38]) - scalars[25] = add(scalars[25], scalars[39]) + scalars[22] = scalars[22] + scalars[36] + scalars[23] = scalars[23] + scalars[37] + scalars[24] = scalars[24] + scalars[38] + scalars[25] = scalars[25] + scalars[39] - scalars[28] = add(scalars[28], scalars[40]) - scalars[29] = add(scalars[29], scalars[41]) - scalars[30] = add(scalars[30], scalars[42]) - scalars[31] = add(scalars[31], scalars[43]) + scalars[28] = scalars[28] + scalars[40] + scalars[29] = scalars[29] + scalars[41] + scalars[30] = scalars[30] + scalars[42] + scalars[31] = scalars[31] + scalars[43] scalars[36] = None scalars[37] = None @@ -766,90 +709,10 @@ def circuit_write_element(elmt: PyFelt | int) -> PyFelt: field = get_base_field(CurveID.GRUMPKIN.value) return field(elmt) if isinstance(elmt, int) else elmt - def circuit_write_elements(elmts: list[PyFelt]) -> list[PyFelt]: - return [circuit_write_element(elmt) for elmt in elmts] - - def from_G1Point(point: G1Point) -> list[PyFelt]: - field = get_base_field(point.curve_id) - return [field(point.x), field(point.y)] - - vk_circuit = HonkVk( - circuit_size=vk.circuit_size, - log_circuit_size=vk.log_circuit_size, - public_inputs_size=vk.public_inputs_size, - public_inputs_offset=circuit_write_element(vk.public_inputs_offset), - qm=from_G1Point(vk.qm), - qc=from_G1Point(vk.qc), - ql=from_G1Point(vk.ql), - qr=from_G1Point(vk.qr), - qo=from_G1Point(vk.qo), - q4=from_G1Point(vk.q4), - qArith=from_G1Point(vk.qArith), - qDeltaRange=from_G1Point(vk.qDeltaRange), - qElliptic=from_G1Point(vk.qElliptic), - qAux=from_G1Point(vk.qAux), - qLookup=from_G1Point(vk.qLookup), - qPoseidon2External=from_G1Point(vk.qPoseidon2External), - qPoseidon2Internal=from_G1Point(vk.qPoseidon2Internal), - s1=from_G1Point(vk.s1), - s2=from_G1Point(vk.s2), - s3=from_G1Point(vk.s3), - s4=from_G1Point(vk.s4), - id1=from_G1Point(vk.id1), - id2=from_G1Point(vk.id2), - id3=from_G1Point(vk.id3), - id4=from_G1Point(vk.id4), - t1=from_G1Point(vk.t1), - t2=from_G1Point(vk.t2), - t3=from_G1Point(vk.t3), - t4=from_G1Point(vk.t4), - lagrange_first=from_G1Point(vk.lagrange_first), - lagrange_last=from_G1Point(vk.lagrange_last), - ) - - proof_circuit = HonkProof( - circuit_size=proof.circuit_size, - public_inputs_size=proof.public_inputs_size, - public_inputs_offset=circuit_write_element(proof.public_inputs_offset), - public_inputs=circuit_write_elements(proof.public_inputs), - w1=from_G1Point(proof.w1), - w2=from_G1Point(proof.w2), - w3=from_G1Point(proof.w3), - w4=from_G1Point(proof.w4), - z_perm=from_G1Point(proof.z_perm), - lookup_read_counts=from_G1Point(proof.lookup_read_counts), - lookup_read_tags=from_G1Point(proof.lookup_read_tags), - lookup_inverses=from_G1Point(proof.lookup_inverses), - sumcheck_univariates=[ - circuit_write_elements(univariate) - for univariate in proof.sumcheck_univariates - ], - sumcheck_evaluations=circuit_write_elements(proof.sumcheck_evaluations), - gemini_fold_comms=[from_G1Point(comm) for comm in proof.gemini_fold_comms], - gemini_a_evaluations=circuit_write_elements(proof.gemini_a_evaluations), - shplonk_q=from_G1Point(proof.shplonk_q), - kzg_quotient=from_G1Point(proof.kzg_quotient), - ) - - tp = HonkTranscript( - eta=circuit_write_element(tp.eta), - etaTwo=circuit_write_element(tp.etaTwo), - etaThree=circuit_write_element(tp.etaThree), - beta=circuit_write_element(tp.beta), - gamma=circuit_write_element(tp.gamma), - alphas=circuit_write_elements(tp.alphas), - gate_challenges=circuit_write_elements(tp.gate_challenges), - sum_check_u_challenges=circuit_write_elements(tp.sum_check_u_challenges), - rho=circuit_write_element(tp.rho), - gemini_r=circuit_write_element(tp.gemini_r), - shplonk_nu=circuit_write_element(tp.shplonk_nu), - shplonk_z=circuit_write_element(tp.shplonk_z), - ) - scalars = circuit_compute_shplemini_msm_scalars( vk.log_circuit_size, - proof_circuit.sumcheck_evaluations, - proof_circuit.gemini_a_evaluations, + [circuit_write_element(elmt) for elmt in proof.sumcheck_evaluations], + [circuit_write_element(elmt) for elmt in proof.gemini_a_evaluations], tp.gemini_r, tp.rho, tp.shplonk_z, @@ -949,57 +812,71 @@ def get_honk_calldata(system: str, vk: Path, proof: Path) -> list[int]: def honk_vk_from_bytes(bytes: bytes) -> HonkVk: + assert len(bytes) == 4 * 8 + 54 * 32 circuit_size = int.from_bytes(bytes[0:8], "big") log_circuit_size = int.from_bytes(bytes[8:16], "big") public_inputs_size = int.from_bytes(bytes[16:24], "big") public_inputs_offset = int.from_bytes(bytes[24:32], "big") - + points = [] cursor = 32 - - rest = bytes[cursor:] - assert len(rest) % 32 == 0 - - # Get all fields that are G1Points from the dataclass - g1_fields = [field.name for field in fields(HonkVk) if field.type == G1Point] - - # Parse all G1Points into a dictionary - points = {} - for field_name in g1_fields: + for i in range(27): x = int.from_bytes(bytes[cursor : cursor + 32], "big") y = int.from_bytes(bytes[cursor + 32 : cursor + 64], "big") - points[field_name] = G1Point(x=x, y=y, curve_id=CurveID.BN254) + points.append(G1Point(x=x, y=y, curve_id=CurveID.BN254)) cursor += 64 - - # Create instance with all parsed values return HonkVk( circuit_size=circuit_size, log_circuit_size=log_circuit_size, public_inputs_size=public_inputs_size, public_inputs_offset=public_inputs_offset, - **points, + qm=points[0], + qc=points[1], + ql=points[2], + qr=points[3], + qo=points[4], + q4=points[5], + qArith=points[6], + qDeltaRange=points[7], + qElliptic=points[8], + qAux=points[9], + qLookup=points[10], + qPoseidon2External=points[11], + qPoseidon2Internal=points[12], + s1=points[13], + s2=points[14], + s3=points[15], + s4=points[16], + id1=points[17], + id2=points[18], + id3=points[19], + id4=points[20], + t1=points[21], + t2=points[22], + t3=points[23], + t4=points[24], + lagrange_first=points[25], + lagrange_last=points[26], ) def honk_proof_from_bytes(bytes: bytes) -> HonkProof: n_elements = int.from_bytes(bytes[:4], "big") - assert len(bytes[4:]) % 32 == 0 + assert len(bytes) == 4 + 32 * n_elements elements = [ int.from_bytes(bytes[i : i + 32], "big") for i in range(4, len(bytes), 32) ] - assert len(elements) == n_elements - + cursor = 0 circuit_size = elements[0] public_inputs_size = elements[1] public_inputs_offset = elements[2] + cursor += 3 MAX_LOG_N = 23 # 2^23 = 8388608 assert circuit_size <= 2**MAX_LOG_N public_inputs = [] - cursor = 3 for i in range(public_inputs_size): public_inputs.append(elements[cursor + i]) - cursor += public_inputs_size def parse_g1_proof_point(i: int) -> G1Point: @@ -1036,7 +913,6 @@ def parse_g1_proof_point(i: int) -> G1Point: # Parse sumcheck_evaluations sumcheck_evaluations = elements[cursor : cursor + NUMBER_OF_ENTITIES] - cursor += NUMBER_OF_ENTITIES # Parse gemini fold comms @@ -1044,21 +920,16 @@ def parse_g1_proof_point(i: int) -> G1Point: parse_g1_proof_point(cursor + i * G1_PROOF_POINT_SIZE) for i in range(CONST_PROOF_SIZE_LOG_N - 1) ] - cursor += (CONST_PROOF_SIZE_LOG_N - 1) * G1_PROOF_POINT_SIZE # Parse gemini a evaluations gemini_a_evaluations = elements[cursor : cursor + CONST_PROOF_SIZE_LOG_N] - cursor += CONST_PROOF_SIZE_LOG_N shplonk_q = parse_g1_proof_point(cursor) kzg_quotient = parse_g1_proof_point(cursor + G1_PROOF_POINT_SIZE) - cursor += 2 * G1_PROOF_POINT_SIZE - assert cursor == len(elements) - return HonkProof( circuit_size=circuit_size, public_inputs_size=public_inputs_size, From 5325a4c28ba8e9fcf9662d29e06c4059c748d7c1 Mon Sep 17 00:00:00 2001 From: Rodrigo Ferreira Date: Fri, 17 Jan 2025 17:57:39 -0300 Subject: [PATCH 4/5] Fixes HonkProof field types --- tests/hydra/test_honk_calldata.py | 38 ++++++++++++++++--------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/tests/hydra/test_honk_calldata.py b/tests/hydra/test_honk_calldata.py index fb916070..f1d803d8 100644 --- a/tests/hydra/test_honk_calldata.py +++ b/tests/hydra/test_honk_calldata.py @@ -179,7 +179,7 @@ class HonkProof: circuit_size: int public_inputs_size: int public_inputs_offset: int - public_inputs: list[int] + public_inputs: list[PyFelt] w1: G1Point w2: G1Point w3: G1Point @@ -188,10 +188,10 @@ class HonkProof: lookup_read_counts: G1Point lookup_read_tags: G1Point lookup_inverses: G1Point - sumcheck_univariates: list[list[int]] - sumcheck_evaluations: list[int] + sumcheck_univariates: list[list[PyFelt]] + sumcheck_evaluations: list[PyFelt] gemini_fold_comms: list[G1Point] - gemini_a_evaluations: list[int] + gemini_a_evaluations: list[PyFelt] shplonk_q: G1Point kzg_quotient: G1Point @@ -241,7 +241,7 @@ def serialize_G1Point256(g1_point: G1Point) -> list[int]: cd.append(proof.public_inputs_offset) cd.extend( bigint_split_array( - x=proof.public_inputs, n_limbs=2, base=2**128, prepend_length=True + x=[elem.value for elem in proof.public_inputs], n_limbs=2, base=2**128, prepend_length=True ) ) cd.extend(serialize_G1Point256(proof.w1)) @@ -254,7 +254,7 @@ def serialize_G1Point256(g1_point: G1Point) -> list[int]: cd.extend(serialize_G1Point256(proof.lookup_inverses)) cd.extend( bigint_split_array( - x=flatten(proof.sumcheck_univariates)[ + x=[elem.value for elem in flatten(proof.sumcheck_univariates)][ : BATCHED_RELATION_PARTIAL_LENGTH * log_circuit_size ], # The rest is 0. n_limbs=2, @@ -265,7 +265,7 @@ def serialize_G1Point256(g1_point: G1Point) -> list[int]: cd.extend( bigint_split_array( - x=proof.sumcheck_evaluations, n_limbs=2, base=2**128, prepend_length=True + x=[elem.value for elem in proof.sumcheck_evaluations], n_limbs=2, base=2**128, prepend_length=True ) ) @@ -275,7 +275,7 @@ def serialize_G1Point256(g1_point: G1Point) -> list[int]: cd.extend( bigint_split_array( - x=proof.gemini_a_evaluations[:log_circuit_size], + x=[elem.value for elem in proof.gemini_a_evaluations[:log_circuit_size]], n_limbs=2, base=2**128, prepend_length=True, @@ -366,7 +366,7 @@ def split_challenge(ch: bytes) -> tuple[int, int]: hasher.update(int.to_bytes(proof.public_inputs_offset, 32, "big")) for pub_input in proof.public_inputs: - hasher.update(int.to_bytes(pub_input, 32, "big")) + hasher.update(int.to_bytes(pub_input.value, 32, "big")) for g1_proof_point in [proof.w1, proof.w2, proof.w3]: x0, x1, y0, y1 = g1_to_g1_proof_point(g1_proof_point) @@ -446,7 +446,7 @@ def split_challenge(ch: bytes) -> tuple[int, int]: # Add the sumcheck univariates for this round for j in range(BATCHED_RELATION_PARTIAL_LENGTH): univariate_chal.append( - int.to_bytes(proof.sumcheck_univariates[i][j], 32, "big") + int.to_bytes(proof.sumcheck_univariates[i][j].value, 32, "big") ) # Update hasher with all univariate challenges @@ -462,7 +462,7 @@ def split_challenge(ch: bytes) -> tuple[int, int]: # Rho challenge : hasher.update(ch4) for i in range(NUMBER_OF_ENTITIES): - hasher.update(int.to_bytes(proof.sumcheck_evaluations[i], 32, "big")) + hasher.update(int.to_bytes(proof.sumcheck_evaluations[i].value, 32, "big")) c5 = hasher.digest_reset() rho, _ = split_challenge(c5) @@ -482,7 +482,7 @@ def split_challenge(ch: bytes) -> tuple[int, int]: # Shplonk Nu : hasher.update(c6) for i in range(CONST_PROOF_SIZE_LOG_N): - hasher.update(int.to_bytes(proof.gemini_a_evaluations[i], 32, "big")) + hasher.update(int.to_bytes(proof.gemini_a_evaluations[i].value, 32, "big")) c7 = hasher.digest_reset() shplonk_nu, _ = split_challenge(c7) @@ -711,8 +711,8 @@ def circuit_write_element(elmt: PyFelt | int) -> PyFelt: scalars = circuit_compute_shplemini_msm_scalars( vk.log_circuit_size, - [circuit_write_element(elmt) for elmt in proof.sumcheck_evaluations], - [circuit_write_element(elmt) for elmt in proof.gemini_a_evaluations], + proof.sumcheck_evaluations, + proof.gemini_a_evaluations, tp.gemini_r, tp.rho, tp.shplonk_z, @@ -874,9 +874,11 @@ def honk_proof_from_bytes(bytes: bytes) -> HonkProof: MAX_LOG_N = 23 # 2^23 = 8388608 assert circuit_size <= 2**MAX_LOG_N + field = get_base_field(CurveID.GRUMPKIN.value) + public_inputs = [] for i in range(public_inputs_size): - public_inputs.append(elements[cursor + i]) + public_inputs.append(field(elements[cursor + i])) cursor += public_inputs_size def parse_g1_proof_point(i: int) -> G1Point: @@ -905,14 +907,14 @@ def parse_g1_proof_point(i: int) -> G1Point: for i in range(CONST_PROOF_SIZE_LOG_N): sumcheck_univariates.append( [ - elements[cursor + i * BATCHED_RELATION_PARTIAL_LENGTH + j] + field(elements[cursor + i * BATCHED_RELATION_PARTIAL_LENGTH + j]) for j in range(BATCHED_RELATION_PARTIAL_LENGTH) ] ) cursor += BATCHED_RELATION_PARTIAL_LENGTH * CONST_PROOF_SIZE_LOG_N # Parse sumcheck_evaluations - sumcheck_evaluations = elements[cursor : cursor + NUMBER_OF_ENTITIES] + sumcheck_evaluations = [field(elem) for elem in elements[cursor : cursor + NUMBER_OF_ENTITIES]] cursor += NUMBER_OF_ENTITIES # Parse gemini fold comms @@ -923,7 +925,7 @@ def parse_g1_proof_point(i: int) -> G1Point: cursor += (CONST_PROOF_SIZE_LOG_N - 1) * G1_PROOF_POINT_SIZE # Parse gemini a evaluations - gemini_a_evaluations = elements[cursor : cursor + CONST_PROOF_SIZE_LOG_N] + gemini_a_evaluations = [field(elem) for elem in elements[cursor : cursor + CONST_PROOF_SIZE_LOG_N]] cursor += CONST_PROOF_SIZE_LOG_N shplonk_q = parse_g1_proof_point(cursor) From 26ce940f5efbec2f1c62ff9df31206b8bff4e77e Mon Sep 17 00:00:00 2001 From: Rodrigo Ferreira Date: Fri, 17 Jan 2025 21:23:37 -0300 Subject: [PATCH 5/5] Saving temporary work on honk_calldata.rs --- tests/hydra/test_honk_calldata.py | 22 +- .../calldata/full_proof_with_hints/groth16.rs | 4 +- tools/garaga_rs/src/calldata/honk_calldata.rs | 678 ++++++++++++++++++ tools/garaga_rs/src/calldata/mod.rs | 1 + tools/garaga_rs/src/calldata/mpc_calldata.rs | 2 +- tools/garaga_rs/src/calldata/msm_calldata.rs | 8 +- tools/garaga_rs/src/python_bindings/msm.rs | 2 +- tools/garaga_rs/src/wasm_bindings.rs | 2 +- 8 files changed, 702 insertions(+), 17 deletions(-) create mode 100644 tools/garaga_rs/src/calldata/honk_calldata.rs diff --git a/tests/hydra/test_honk_calldata.py b/tests/hydra/test_honk_calldata.py index f1d803d8..a5f8d51d 100644 --- a/tests/hydra/test_honk_calldata.py +++ b/tests/hydra/test_honk_calldata.py @@ -241,7 +241,10 @@ def serialize_G1Point256(g1_point: G1Point) -> list[int]: cd.append(proof.public_inputs_offset) cd.extend( bigint_split_array( - x=[elem.value for elem in proof.public_inputs], n_limbs=2, base=2**128, prepend_length=True + x=[elem.value for elem in proof.public_inputs], + n_limbs=2, + base=2**128, + prepend_length=True, ) ) cd.extend(serialize_G1Point256(proof.w1)) @@ -265,7 +268,10 @@ def serialize_G1Point256(g1_point: G1Point) -> list[int]: cd.extend( bigint_split_array( - x=[elem.value for elem in proof.sumcheck_evaluations], n_limbs=2, base=2**128, prepend_length=True + x=[elem.value for elem in proof.sumcheck_evaluations], + n_limbs=2, + base=2**128, + prepend_length=True, ) ) @@ -705,10 +711,6 @@ def get_ultra_flavor_honk_calldata_from_vk_and_proof( ) -> list[int]: tp = honk_transcript_from_proof(system, proof) - def circuit_write_element(elmt: PyFelt | int) -> PyFelt: - field = get_base_field(CurveID.GRUMPKIN.value) - return field(elmt) if isinstance(elmt, int) else elmt - scalars = circuit_compute_shplemini_msm_scalars( vk.log_circuit_size, proof.sumcheck_evaluations, @@ -914,7 +916,9 @@ def parse_g1_proof_point(i: int) -> G1Point: cursor += BATCHED_RELATION_PARTIAL_LENGTH * CONST_PROOF_SIZE_LOG_N # Parse sumcheck_evaluations - sumcheck_evaluations = [field(elem) for elem in elements[cursor : cursor + NUMBER_OF_ENTITIES]] + sumcheck_evaluations = [ + field(elem) for elem in elements[cursor : cursor + NUMBER_OF_ENTITIES] + ] cursor += NUMBER_OF_ENTITIES # Parse gemini fold comms @@ -925,7 +929,9 @@ def parse_g1_proof_point(i: int) -> G1Point: cursor += (CONST_PROOF_SIZE_LOG_N - 1) * G1_PROOF_POINT_SIZE # Parse gemini a evaluations - gemini_a_evaluations = [field(elem) for elem in elements[cursor : cursor + CONST_PROOF_SIZE_LOG_N]] + gemini_a_evaluations = [ + field(elem) for elem in elements[cursor : cursor + CONST_PROOF_SIZE_LOG_N] + ] cursor += CONST_PROOF_SIZE_LOG_N shplonk_q = parse_g1_proof_point(cursor) diff --git a/tools/garaga_rs/src/calldata/full_proof_with_hints/groth16.rs b/tools/garaga_rs/src/calldata/full_proof_with_hints/groth16.rs index 55e2db7b..af571d8a 100644 --- a/tools/garaga_rs/src/calldata/full_proof_with_hints/groth16.rs +++ b/tools/garaga_rs/src/calldata/full_proof_with_hints/groth16.rs @@ -209,7 +209,7 @@ pub fn get_groth16_calldata( .collect::>(), &proof.public_inputs, curve_id as usize, - true, + Some(true), false, true, false, @@ -226,7 +226,7 @@ pub fn get_groth16_calldata( proof.public_inputs[3].clone(), ], curve_id as usize, - true, + Some(true), false, true, true, diff --git a/tools/garaga_rs/src/calldata/honk_calldata.rs b/tools/garaga_rs/src/calldata/honk_calldata.rs new file mode 100644 index 00000000..b9c94ea0 --- /dev/null +++ b/tools/garaga_rs/src/calldata/honk_calldata.rs @@ -0,0 +1,678 @@ +use crate::algebra::g1g2pair::G1G2Pair; +use crate::algebra::g1point::G1Point; +use crate::calldata::mpc_calldata; +use crate::calldata::msm_calldata; +use crate::definitions::BN254PrimeField; +use crate::definitions::CurveID; +use crate::definitions::FieldElement; +use crate::definitions::GrumpkinPrimeField; +use crate::definitions::Stark252PrimeField; +use num_bigint::BigUint; + +const BATCHED_RELATION_PARTIAL_LENGTH: usize = 8; +const CONST_PROOF_SIZE_LOG_N: usize = 28; +const NUMBER_OF_SUBRELATIONS: usize = 26; +const NUMBER_OF_ALPHAS: usize = NUMBER_OF_SUBRELATIONS - 1; +const NUMBER_OF_ENTITIES: usize = 44; + +pub enum HonkFlavor { + KECCAK = 0, + STARKNET = 1, +} + +pub struct HonkVk { + pub circuit_size: u64, + pub log_circuit_size: u64, + pub public_inputs_size: u64, + pub public_inputs_offset: u64, + pub qm: G1Point, + pub qc: G1Point, + pub ql: G1Point, + pub qr: G1Point, + pub qo: G1Point, + pub q4: G1Point, + pub q_arith: G1Point, + pub q_delta_range: G1Point, + pub q_elliptic: G1Point, + pub q_aux: G1Point, + pub q_lookup: G1Point, + pub q_poseidon2_external: G1Point, + pub q_poseidon2_internal: G1Point, + pub s1: G1Point, + pub s2: G1Point, + pub s3: G1Point, + pub s4: G1Point, + pub id1: G1Point, + pub id2: G1Point, + pub id3: G1Point, + pub id4: G1Point, + pub t1: G1Point, + pub t2: G1Point, + pub t3: G1Point, + pub t4: G1Point, + pub lagrange_first: G1Point, + pub lagrange_last: G1Point, +} + +pub struct HonkProof { + pub circuit_size: u64, + pub public_inputs_size: u64, + pub public_inputs_offset: u64, + pub public_inputs: Vec>, + pub w1: G1Point, + pub w2: G1Point, + pub w3: G1Point, + pub w4: G1Point, + pub z_perm: G1Point, + pub lookup_read_counts: G1Point, + pub lookup_read_tags: G1Point, + pub lookup_inverses: G1Point, + pub sumcheck_univariates: [[FieldElement; BATCHED_RELATION_PARTIAL_LENGTH]; + CONST_PROOF_SIZE_LOG_N], + pub sumcheck_evaluations: [FieldElement; NUMBER_OF_ENTITIES], + pub gemini_fold_comms: [G1Point; CONST_PROOF_SIZE_LOG_N - 1], + pub gemini_a_evaluations: [FieldElement; CONST_PROOF_SIZE_LOG_N], + pub shplonk_q: G1Point, + pub kzg_quotient: G1Point, +} + +pub struct HonkTranscript { + pub eta: FieldElement, + pub eta_two: FieldElement, + pub eta_three: FieldElement, + pub beta: FieldElement, + pub gamma: FieldElement, + pub alphas: [FieldElement; NUMBER_OF_ALPHAS], + pub gate_challenges: [FieldElement; CONST_PROOF_SIZE_LOG_N], + pub sum_check_u_challenges: [FieldElement; CONST_PROOF_SIZE_LOG_N], + pub rho: FieldElement, + pub gemini_r: FieldElement, + pub shplonk_nu: FieldElement, + pub shplonk_z: FieldElement, +} + +pub fn get_ultra_flavor_honk_calldata_from_vk_and_proof( + flavor: HonkFlavor, + vk: HonkVk, + proof: HonkProof, +) -> Vec { + let tp = honk_transcript_from_proof(flavor, &proof); + + let proof_data = serialize_honk_proof_to_calldata(&proof); + + let scalars = circuit_compute_shplemini_msm_scalars( + vk.log_circuit_size, + &proof.sumcheck_evaluations, + &proof.gemini_a_evaluations, + &tp.gemini_r, + &tp.rho, + &tp.shplonk_z, + &tp.shplonk_nu, + &tp.sum_check_u_challenges, + ); + + let scalars_msm = extract_msm_scalars(&scalars, vk.log_circuit_size); + + let mut points = vec![ + vk.qm, // 1 + vk.qc, // 2 + vk.ql, // 3 + vk.qr, // 4 + vk.qo, // 5 + vk.q4, // 6 + vk.q_arith, // 7 + vk.q_delta_range, // 8 + vk.q_elliptic, // 9 + vk.q_aux, // 10 + vk.q_lookup, // 11 + vk.q_poseidon2_external, // 12 + vk.q_poseidon2_internal, // 13 + vk.s1, // 14 + vk.s2, // 15 + vk.s3, // 16 + vk.s4, // 17 + vk.id1, // 18 + vk.id2, // 19 + vk.id3, // 20 + vk.id4, // 21 + vk.t1, // 22 + vk.t2, // 23 + vk.t3, // 24 + vk.t4, // 25 + vk.lagrange_first, // 26 + vk.lagrange_last, // 27 + proof.w1, // 28 + proof.w2, // 29 + proof.w3, // 30 + proof.w4, // 31 + proof.z_perm.clone(), // 32 + proof.lookup_inverses, // 33 + proof.lookup_read_counts, // 34 + proof.lookup_read_tags, // 35 + proof.z_perm, // 44 + ]; + points.extend(proof.gemini_fold_comms[0..(vk.log_circuit_size - 1) as usize].to_vec()); + //points.append(G1Point.get_nG(CurveID.BN254, 1)) + points.push(proof.kzg_quotient.clone()); + + let msm_data = msm_calldata::calldata_builder( + &points, + &scalars_msm, + CurveID::BN254 as usize, + None, + false, + false, + false, + ); + + //G2_POINT_KZG_1 = G2Point.get_nG(CurveID.BN254, 1) + //G2_POINT_KZG_2 = G2Point( + // x=( + // 0x0118C4D5B837BCC2BC89B5B398B5974E9F5944073B32078B7E231FEC938883B0, + // 0x260E01B251F6F1C7E7FF4E580791DEE8EA51D87A358E038B4EFE30FAC09383C1, + // ), + // y=( + // 0x22FEBDA3C0C0632A56475B4214E5615E11E6DD3F96E6CEA2854A87D4DACC5E55, + // 0x04FC6369F7110FE3D25156C1BB9A72859CF2A04641F99BA4EE413C80DA6A5FE4, + // ), + // curve_id=CurveID.BN254, + //) + + //P_0 = G1Point.msm(points=points, scalars=scalars_msm).add(proof.shplonk_q) + let _p_1 = proof.kzg_quotient.neg(); + + use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bn_254::field_extension::Degree2ExtensionField; + let pairs: Vec> = vec![]; + //pairs = [G1G2Pair(P_0, G2_POINT_KZG_1), G1G2Pair(P_1, G2_POINT_KZG_2)] + + let mpc_data = { + use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bn_254::field_extension::Degree12ExtensionField; + use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bn_254::field_extension::Degree2ExtensionField; + use lambdaworks_math::elliptic_curve::short_weierstrass::curves::bn_254::field_extension::Degree6ExtensionField; + mpc_calldata::calldata_builder::< + true, + BN254PrimeField, + Degree2ExtensionField, + Degree6ExtensionField, + Degree12ExtensionField, + >(&pairs, 2, &None) + .unwrap() + }; + + let size = proof_data.len() + msm_data.len() + mpc_data.len(); + let mut call_data: Vec = vec![size.into()]; + call_data.extend(proof_data); + call_data.extend(msm_data); + call_data.extend(mpc_data); + call_data +} + +fn serialize_honk_proof_to_calldata(_proof: &HonkProof) -> Vec { + /* + def serialize_G1Point256(g1_point: G1Point) -> list[int]: + xl, xh = split_128(g1_point.x) + yl, yh = split_128(g1_point.y) + return [xl, xh, yl, yh] + + log_circuit_size = int(math.log2(proof.circuit_size)) + + cd = [] + cd.append(proof.circuit_size) + cd.append(proof.public_inputs_size) + cd.append(proof.public_inputs_offset) + cd.extend( + bigint_split_array( + x=[elem.value for elem in proof.public_inputs], n_limbs=2, base=2**128, prepend_length=True + ) + ) + cd.extend(serialize_G1Point256(proof.w1)) + cd.extend(serialize_G1Point256(proof.w2)) + cd.extend(serialize_G1Point256(proof.w3)) + cd.extend(serialize_G1Point256(proof.w4)) + cd.extend(serialize_G1Point256(proof.z_perm)) + cd.extend(serialize_G1Point256(proof.lookup_read_counts)) + cd.extend(serialize_G1Point256(proof.lookup_read_tags)) + cd.extend(serialize_G1Point256(proof.lookup_inverses)) + cd.extend( + bigint_split_array( + x=[elem.value for elem in flatten(proof.sumcheck_univariates)][ + : BATCHED_RELATION_PARTIAL_LENGTH * log_circuit_size + ], # The rest is 0. + n_limbs=2, + base=2**128, + prepend_length=True, + ) + ) + + cd.extend( + bigint_split_array( + x=[elem.value for elem in proof.sumcheck_evaluations], n_limbs=2, base=2**128, prepend_length=True + ) + ) + + cd.append(log_circuit_size - 1) + for pt in proof.gemini_fold_comms[: log_circuit_size - 1]: # The rest is G(1, 2) + cd.extend(serialize_G1Point256(pt)) + + cd.extend( + bigint_split_array( + x=[elem.value for elem in proof.gemini_a_evaluations[:log_circuit_size]], + n_limbs=2, + base=2**128, + prepend_length=True, + ) + ) + cd.extend(serialize_G1Point256(proof.shplonk_q)) + cd.extend(serialize_G1Point256(proof.kzg_quotient)) + + return cd + */ + todo!() +} + +fn extract_msm_scalars(_scalars: &[Option], _log_n: u64) -> Vec { + /* + assert len(scalars) == NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2 + + start_dummy = NUMBER_OF_ENTITIES + log_n + end_dummy = NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + + scalars_no_dummy = scalars[:start_dummy] + scalars[end_dummy:] + + scalars_filtered = scalars_no_dummy[1:] + scalars_filtered_no_nones = [ + scalar for scalar in scalars_filtered if scalar is not None + ] + return [s.value for s in scalars_filtered_no_nones] + */ + todo!() +} + +pub trait Hasher { + fn reset(&mut self); + fn update(&mut self, data: &[u8]); + fn digest(&self) -> FieldElement; + fn digest_reset(&mut self) -> FieldElement { + let result = self.digest(); + self.reset(); + return result; + } + fn update_element(&mut self, _element: &FieldElement) { + todo!() + } + fn update_point(&mut self, _point: &G1Point) { + todo!() + } +} + +pub struct KeccakHasher {} + +impl KeccakHasher { + fn new() -> Self { + todo!() + } +} + +impl Hasher for KeccakHasher { + fn reset(&mut self) { + todo!() + } + fn update(&mut self, _data: &[u8]) { + todo!() + } + fn digest(&self) -> FieldElement { + todo!() + } +} + +pub struct StarknetHasher { + pub state: [FieldElement; 3], +} + +impl StarknetHasher { + fn new() -> Self { + todo!() + } +} + +impl Hasher for StarknetHasher { + fn reset(&mut self) { + todo!() + } + fn update(&mut self, _data: &[u8]) { + todo!() + } + fn digest(&self) -> FieldElement { + todo!() + } +} + +fn honk_transcript_from_proof(flavor: HonkFlavor, proof: &HonkProof) -> HonkTranscript { + match flavor { + HonkFlavor::KECCAK => compute_honk_transcript_from_proof(KeccakHasher::new(), proof), + HonkFlavor::STARKNET => compute_honk_transcript_from_proof(StarknetHasher::new(), proof), + } +} + +fn compute_honk_transcript_from_proof( + mut hasher: T, + proof: &HonkProof, +) -> HonkTranscript { + // Round 0 : circuit_size, public_inputs_size, public_input_offset, [public_inputs], w1, w2, w3 + + /* + hasher.update(int.to_bytes(proof.circuit_size, 32, "big")) + hasher.update(int.to_bytes(proof.public_inputs_size, 32, "big")) + hasher.update(int.to_bytes(proof.public_inputs_offset, 32, "big")) + */ + + for public_input in &proof.public_inputs { + hasher.update_element(public_input); + } + hasher.update_point(&proof.w1); + hasher.update_point(&proof.w2); + hasher.update_point(&proof.w3); + + let ch0 = hasher.digest_reset(); + /* + eta, eta_two = split_challenge(ch0) + */ + hasher.update_element(&ch0); + let ch0 = hasher.digest_reset(); + /* + eta_three, _ = split_challenge(ch0) + */ + + // Round 1 : ch0, lookup_read_counts, lookup_read_tags, w4 + hasher.update_element(&ch0); + hasher.update_point(&proof.lookup_read_counts); + hasher.update_point(&proof.lookup_read_tags); + hasher.update_point(&proof.w4); + let ch1 = hasher.digest_reset(); + /* + beta, gamma = split_challenge(ch1) + */ + + // Round 2: ch1, lookup_inverses, z_perm + hasher.update_element(&ch1); + hasher.update_point(&proof.lookup_inverses); + hasher.update_point(&proof.z_perm); + let mut ch2 = hasher.digest_reset(); + /* + alphas = [None] * NUMBER_OF_ALPHAS + alphas[0], alphas[1] = split_challenge(ch2) + */ + for _i in 1..NUMBER_OF_ALPHAS / 2 { + hasher.update_element(&ch2); + ch2 = hasher.digest_reset(); + /* + alphas[i * 2], alphas[i * 2 + 1] = split_challenge(ch2) + */ + } + + if NUMBER_OF_ALPHAS % 2 == 1 { + hasher.update_element(&ch2); + ch2 = hasher.digest_reset(); + /* + alphas[-1], _ = split_challenge(ch2) + */ + } + + // Round 3: Gate Challenges : + let mut ch3 = ch2; + /* + gate_challenges = [None] * CONST_PROOF_SIZE_LOG_N + */ + for _i in 0..CONST_PROOF_SIZE_LOG_N { + hasher.update_element(&ch3); + ch3 = hasher.digest_reset(); + /* + gate_challenges[i], _ = split_challenge(ch3) + */ + } + + /* + # Round 4: Sumcheck u challenges + ch4 = ch3 + sum_check_u_challenges = [None] * CONST_PROOF_SIZE_LOG_N + + for i in range(CONST_PROOF_SIZE_LOG_N): + # Create array of univariate challenges starting with previous challenge + univariate_chal = [ch4] + + # Add the sumcheck univariates for this round + for j in range(BATCHED_RELATION_PARTIAL_LENGTH): + univariate_chal.append( + int.to_bytes(proof.sumcheck_univariates[i][j].value, 32, "big") + ) + + # Update hasher with all univariate challenges + for chal in univariate_chal: + hasher.update(chal) + + # Get next challenge + ch4 = hasher.digest_reset() + + # Split challenge to get sumcheck challenge + sum_check_u_challenges[i], _ = split_challenge(ch4) + + # Rho challenge : + hasher.update(ch4) + for i in range(NUMBER_OF_ENTITIES): + hasher.update(int.to_bytes(proof.sumcheck_evaluations[i].value, 32, "big")) + + c5 = hasher.digest_reset() + rho, _ = split_challenge(c5) + + # Gemini R : + hasher.update(c5) + for i in range(CONST_PROOF_SIZE_LOG_N - 1): + x0, x1, y0, y1 = g1_to_g1_proof_point(proof.gemini_fold_comms[i]) + hasher.update(int.to_bytes(x0, 32, "big")) + hasher.update(int.to_bytes(x1, 32, "big")) + hasher.update(int.to_bytes(y0, 32, "big")) + hasher.update(int.to_bytes(y1, 32, "big")) + + c6 = hasher.digest_reset() + gemini_r, _ = split_challenge(c6) + + # Shplonk Nu : + hasher.update(c6) + for i in range(CONST_PROOF_SIZE_LOG_N): + hasher.update(int.to_bytes(proof.gemini_a_evaluations[i].value, 32, "big")) + + c7 = hasher.digest_reset() + shplonk_nu, _ = split_challenge(c7) + + # Shplonk Z : + hasher.update(c7) + x0, x1, y0, y1 = g1_to_g1_proof_point(proof.shplonk_q) + hasher.update(int.to_bytes(x0, 32, "big")) + hasher.update(int.to_bytes(x1, 32, "big")) + hasher.update(int.to_bytes(y0, 32, "big")) + hasher.update(int.to_bytes(y1, 32, "big")) + + c8 = hasher.digest_reset() + shplonk_z, _ = split_challenge(c8) + + field = get_base_field(CurveID.GRUMPKIN) + return HonkTranscript( + eta=field(eta), + etaTwo=field(eta_two), + etaThree=field(eta_three), + beta=field(beta), + gamma=field(gamma), + alphas=[field(alpha) for alpha in alphas], + gate_challenges=[field(gate_challenge) for gate_challenge in gate_challenges], + sum_check_u_challenges=[ + field(sum_check_u_challenge) + for sum_check_u_challenge in sum_check_u_challenges + ], + rho=field(rho), + gemini_r=field(gemini_r), + shplonk_nu=field(shplonk_nu), + shplonk_z=field(shplonk_z), + ) + */ + todo!() +} + +fn circuit_compute_shplemini_msm_scalars( + _log_n: u64, + _p_sumcheck_evaluations: &[FieldElement; NUMBER_OF_ENTITIES], + _p_gemini_a_evaluations: &[FieldElement; CONST_PROOF_SIZE_LOG_N], + _tp_gemini_r: &FieldElement, + _tp_rho: &FieldElement, + _tp_shplonk_z: &FieldElement, + _tp_shplonk_nu: &FieldElement, + _tp_sum_check_u_challenges: &[FieldElement; CONST_PROOF_SIZE_LOG_N], +) -> [Option; NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2] { + /* + field = get_base_field(CurveID.GRUMPKIN) + + powers_of_evaluations_challenge = [tp_gemini_r] + for i in range(1, log_n): + powers_of_evaluations_challenge.append( + powers_of_evaluations_challenge[i - 1] + * powers_of_evaluations_challenge[i - 1] + ) + + scalars = [field(0)] * (NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 2) + + # computeInvertedGeminiDenominators + + inverse_vanishing_evals = [None] * (CONST_PROOF_SIZE_LOG_N + 1) + inverse_vanishing_evals[0] = ( + tp_shplonk_z - powers_of_evaluations_challenge[0] + ).__inv__() + for i in range(log_n): + inverse_vanishing_evals[i + 1] = ( + tp_shplonk_z + powers_of_evaluations_challenge[i] + ).__inv__() + assert len(inverse_vanishing_evals) == CONST_PROOF_SIZE_LOG_N + 1 + + unshifted_scalar = -( + inverse_vanishing_evals[0] + tp_shplonk_nu * inverse_vanishing_evals[1] + ) + + shifted_scalar = -( + tp_gemini_r.__inv__() + * (inverse_vanishing_evals[0] - tp_shplonk_nu * inverse_vanishing_evals[1]) + ) + + scalars[0] = field(1) + + batching_challenge = field(1) + batched_evaluation = field(0) + + for i in range(1, NUMBER_UNSHIFTED + 1): + scalars[i] = unshifted_scalar * batching_challenge + batched_evaluation = ( + batched_evaluation + p_sumcheck_evaluations[i - 1] * batching_challenge + ) + batching_challenge = batching_challenge * tp_rho + + for i in range(NUMBER_UNSHIFTED + 1, NUMBER_OF_ENTITIES + 1): + scalars[i] = shifted_scalar * batching_challenge + batched_evaluation = ( + batched_evaluation + p_sumcheck_evaluations[i - 1] * batching_challenge + ) + # skip last round: + if i < NUMBER_OF_ENTITIES: + batching_challenge = batching_challenge * tp_rho + + constant_term_accumulator = field(0) + batching_challenge = tp_shplonk_nu * tp_shplonk_nu + + for i in range(CONST_PROOF_SIZE_LOG_N - 1): + dummy_round = i >= (log_n - 1) + + scaling_factor = field(0) + if not dummy_round: + scaling_factor = batching_challenge * inverse_vanishing_evals[i + 2] + scalars[NUMBER_OF_ENTITIES + i + 1] = -scaling_factor + constant_term_accumulator = ( + constant_term_accumulator + + scaling_factor * p_gemini_a_evaluations[i + 1] + ) + + # skip last round: + if i < log_n - 2: + batching_challenge = batching_challenge * tp_shplonk_nu + + # computeGeminiBatchedUnivariateEvaluation + def compute_gemini_batched_univariate_evaluation( + tp_sumcheck_u_challenges, + batched_eval_accumulator, + gemini_evaluations, + gemini_eval_challenge_powers, + ): + for i in range(log_n, 0, -1): + challenge_power = gemini_eval_challenge_powers[i - 1] + u = tp_sumcheck_u_challenges[i - 1] + eval_neg = gemini_evaluations[i - 1] + + term = challenge_power * (field(1) - u) + + batched_eval_round_acc = ( + field(2) * challenge_power * batched_eval_accumulator + ) - (eval_neg * (term - u)) + + den = term + u + + batched_eval_round_acc = batched_eval_round_acc * den.__inv__() + batched_eval_accumulator = batched_eval_round_acc + + return batched_eval_accumulator + + a_0_pos = compute_gemini_batched_univariate_evaluation( + tp_sumcheck_u_challenges, + batched_evaluation, + p_gemini_a_evaluations, + powers_of_evaluations_challenge, + ) + + constant_term_accumulator = ( + constant_term_accumulator + a_0_pos * inverse_vanishing_evals[0] + ) + + constant_term_accumulator = ( + constant_term_accumulator + + p_gemini_a_evaluations[0] * tp_shplonk_nu * inverse_vanishing_evals[1] + ) + + scalars[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N] = constant_term_accumulator + scalars[NUMBER_OF_ENTITIES + CONST_PROOF_SIZE_LOG_N + 1] = tp_shplonk_z + + # vk.t1 : 22 + 36 + # vk.t2 : 23 + 37 + # vk.t3 : 24 + 38 + # vk.t4 : 25 + 39 + + # proof.w1 : 28 + 40 + # proof.w2 : 29 + 41 + # proof.w3 : 30 + 42 + # proof.w4 : 31 + 43 + + scalars[22] = scalars[22] + scalars[36] + scalars[23] = scalars[23] + scalars[37] + scalars[24] = scalars[24] + scalars[38] + scalars[25] = scalars[25] + scalars[39] + + scalars[28] = scalars[28] + scalars[40] + scalars[29] = scalars[29] + scalars[41] + scalars[30] = scalars[30] + scalars[42] + scalars[31] = scalars[31] + scalars[43] + + scalars[36] = None + scalars[37] = None + scalars[38] = None + scalars[39] = None + scalars[40] = None + scalars[41] = None + scalars[42] = None + scalars[43] = None + + return scalars*/ + todo!() +} diff --git a/tools/garaga_rs/src/calldata/mod.rs b/tools/garaga_rs/src/calldata/mod.rs index 59a417bd..8dd4e4a1 100644 --- a/tools/garaga_rs/src/calldata/mod.rs +++ b/tools/garaga_rs/src/calldata/mod.rs @@ -1,4 +1,5 @@ pub mod full_proof_with_hints; +pub mod honk_calldata; pub mod mpc_calldata; pub mod msm_calldata; diff --git a/tools/garaga_rs/src/calldata/mpc_calldata.rs b/tools/garaga_rs/src/calldata/mpc_calldata.rs index 4bf88b8d..d94fa894 100644 --- a/tools/garaga_rs/src/calldata/mpc_calldata.rs +++ b/tools/garaga_rs/src/calldata/mpc_calldata.rs @@ -287,7 +287,7 @@ where ) } -fn calldata_builder( +pub fn calldata_builder( pairs: &[G1G2Pair], n_fixed_g2: usize, public_pair: &Option>, diff --git a/tools/garaga_rs/src/calldata/msm_calldata.rs b/tools/garaga_rs/src/calldata/msm_calldata.rs index b186f6a9..4b17a07e 100644 --- a/tools/garaga_rs/src/calldata/msm_calldata.rs +++ b/tools/garaga_rs/src/calldata/msm_calldata.rs @@ -26,7 +26,7 @@ pub fn msm_calldata_builder( values: &[BigUint], scalars: &[BigUint], curve_id: usize, - include_digits_decomposition: bool, + include_digits_decomposition: Option, include_points_and_scalars: bool, serialize_as_pure_felt252_array: bool, risc0_mode: bool, @@ -97,7 +97,7 @@ fn handle_curve( values: &[BigUint], scalars: &[BigUint], curve_id: usize, - include_digits_decomposition: bool, + include_digits_decomposition: Option, include_points_and_scalars: bool, serialize_as_pure_felt252_array: bool, risc0_mode: bool, @@ -135,7 +135,7 @@ pub fn calldata_builder>( points: &[G1Point], scalars: &[BigUint], curve_id: usize, - include_digits_decomposition: bool, + include_digits_decomposition: Option, include_points_and_scalars: bool, serialize_as_pure_felt252_array: bool, risc0_mode: bool, @@ -219,7 +219,7 @@ where } // scalars_digits_decompositions - { + if let Some(include_digits_decomposition) = include_digits_decomposition { let flag: usize = if include_digits_decomposition { 0 } else { 1 }; push(call_data_ref, flag); if include_digits_decomposition { diff --git a/tools/garaga_rs/src/python_bindings/msm.rs b/tools/garaga_rs/src/python_bindings/msm.rs index c40896c2..a3e3afb3 100644 --- a/tools/garaga_rs/src/python_bindings/msm.rs +++ b/tools/garaga_rs/src/python_bindings/msm.rs @@ -24,7 +24,7 @@ pub fn msm_calldata_builder( &values, &scalars, curve_id, - include_digits_decomposition, + Some(include_digits_decomposition), include_points_and_scalars, serialize_as_pure_felt252_array, risc0_mode, diff --git a/tools/garaga_rs/src/wasm_bindings.rs b/tools/garaga_rs/src/wasm_bindings.rs index dbae9eaf..d458194b 100644 --- a/tools/garaga_rs/src/wasm_bindings.rs +++ b/tools/garaga_rs/src/wasm_bindings.rs @@ -33,7 +33,7 @@ pub fn msm_calldata_builder( &values, &scalars, curve_id, - include_digits_decomposition, + Some(include_digits_decomposition), include_points_and_scalars, serialize_as_pure_felt252_array, risc0_mode,