@@ -14,7 +14,7 @@ use plonky2::{
1414 witness:: { PartitionWitness , Witness , WitnessWrite } ,
1515 } ,
1616 plonk:: { circuit_builder:: CircuitBuilder , circuit_data:: CommonCircuitData } ,
17- util:: serialization:: { IoResult , Read , Write } ,
17+ util:: serialization:: { Buffer , IoResult , Read , Write } ,
1818} ;
1919
2020use crate :: backends:: plonky2:: basetypes:: { D , F } ;
@@ -79,7 +79,10 @@ impl<F: RichField + Extendable<D>, const D: usize> SimpleGenerator<F, D>
7979/// A big integer, represented in base `2^32` with 10 digits, in little endian
8080/// form.
8181#[ derive( Clone , Debug ) ]
82- pub struct BigUInt320Target ( pub ( super ) [ Target ; 10 ] ) ;
82+ pub struct BigUInt320Target {
83+ pub limbs : [ Target ; 10 ] ,
84+ pub bits : [ BoolTarget ; 320 ] ,
85+ }
8386
8487pub trait CircuitBuilderBits {
8588 /// Enforces the constraint that `then_zero` must be zero if `if_zero`
@@ -90,27 +93,26 @@ pub trait CircuitBuilderBits {
9093 /// are zero, then it chooses the solution `x = 0`.
9194 fn conditional_zero ( & mut self , if_zero : Target , then_zero : Target ) ;
9295
93- /// Returns the binary representation of the target, in little-endian order.
94- fn biguint_bits ( & mut self , x : & BigUInt320Target ) -> [ BoolTarget ; 320 ] ;
95-
9696 /// Decomposes the target x as `y + 2^32 z`, where `0 < y,z < 2**32`, and
9797 /// `y=0` if `z=2**32-1`. Note that calling [`CircuitBuilder::split_le`]
9898 /// with `num_bits = 64` will not check the latter condition.
9999 fn split_32_bit ( & mut self , x : Target ) -> [ Target ; 2 ] ;
100100
101+ /// Like `split_low_high` except it doesn't discard the bit decompositions.
102+ fn split_low_high_with_bits (
103+ & mut self ,
104+ x : Target ,
105+ n_log : usize ,
106+ num_bits : usize ,
107+ ) -> ( ( Target , Vec < BoolTarget > ) , ( Target , Vec < BoolTarget > ) ) ;
108+
101109 /// Interprets `arr` as an integer in base `[GoldilocksField::ORDER]`,
102110 /// with the digits in little endian order. The length of `arr` must be at
103111 /// most 5.
104112 fn field_elements_to_biguint ( & mut self , arr : & [ Target ] ) -> BigUInt320Target ;
105113
106- fn normalize_bigint (
107- & mut self ,
108- x : & BigUInt320Target ,
109- max_digit_bits : usize ,
110- max_num_carries : usize ,
111- ) -> BigUInt320Target ;
112-
113114 fn constant_biguint320 ( & mut self , n : & BigUint ) -> BigUInt320Target ;
115+ fn biguint320_target_from_limbs ( & mut self , x : & [ Target ] ) -> BigUInt320Target ;
114116 fn add_virtual_biguint320_target ( & mut self ) -> BigUInt320Target ;
115117 fn connect_biguint320 ( & mut self , x : & BigUInt320Target , y : & BigUInt320Target ) ;
116118}
@@ -128,20 +130,18 @@ impl CircuitBuilderBits for CircuitBuilder<GoldilocksField, 2> {
128130 self . connect ( prod, then_zero) ;
129131 }
130132
131- fn biguint_bits ( & mut self , x : & BigUInt320Target ) -> [ BoolTarget ; 320 ] {
132- let bits = x. 0 . map ( |t| self . low_bits ( t, 32 , 32 ) ) ;
133- array:: from_fn ( |i| bits[ i / 32 ] [ i % 32 ] )
134- }
135-
136133 fn field_elements_to_biguint ( & mut self , arr : & [ Target ] ) -> BigUInt320Target {
137134 assert ! ( arr. len( ) <= 5 ) ;
138135 let zero = self . zero ( ) ;
139136 let neg_one = self . neg_one ( ) ;
140137 let two_32 = self . constant ( GoldilocksField :: from_canonical_u64 ( 1 << 32 ) ) ;
141138 // Apply Horner's method to Σarr[i]*p^i.
142139 // First map each target to its limbs.
143- let arr_limbs: Vec < _ > = arr. iter ( ) . map ( |x| self . split_32_bit ( * x) . to_vec ( ) ) . collect ( ) ;
144- let res_limbs = arr_limbs
140+ let arr_limbs: Vec < _ > = arr
141+ . iter ( )
142+ . map ( |x| ( self . split_32_bit ( * x) . to_vec ( ) , vec ! [ ] ) )
143+ . collect ( ) ;
144+ let ( res_limbs, res_bits) = arr_limbs
145145 . into_iter ( )
146146 . rev ( )
147147 . enumerate ( )
@@ -153,25 +153,25 @@ impl CircuitBuilderBits for CircuitBuilder<GoldilocksField, 2> {
153153 . map ( |j| {
154154 if j == 0 {
155155 // x_0
156- res[ 0 ]
156+ res. 0 [ 0 ]
157157 } else if j == 1 {
158158 // x_1 - x_0 + 2^32
159- let diff = self . sub ( res[ 1 ] , res[ 0 ] ) ;
159+ let diff = self . sub ( res. 0 [ 1 ] , res. 0 [ 0 ] ) ;
160160 self . add ( diff, two_32)
161161 } else if j < 2 * i {
162162 // x_j + x_{j-2} - x_{j-1} + 2^32 - 1
163- let diff = self . sub ( res[ j] , res[ j - 1 ] ) ;
164- let sum = self . add ( diff, res[ j - 2 ] ) ;
163+ let diff = self . sub ( res. 0 [ j] , res. 0 [ j - 1 ] ) ;
164+ let sum = self . add ( diff, res. 0 [ j - 2 ] ) ;
165165 let sum = self . add ( sum, two_32) ;
166166 self . add ( sum, neg_one)
167167 } else if j == 2 * i {
168168 // x_{2*j - 2} - x_{2*j - 1} + 2^32
169- let diff = self . sub ( res[ 2 * i - 2 ] , res[ 2 * i - 1 ] ) ;
169+ let diff = self . sub ( res. 0 [ 2 * i - 2 ] , res. 0 [ 2 * i - 1 ] ) ;
170170 let sum = self . add ( diff, two_32) ;
171171 self . add ( sum, neg_one)
172172 } else {
173173 // x_{2*i - 1} - 1
174- self . add ( res[ 2 * i - 1 ] , neg_one)
174+ self . add ( res. 0 [ 2 * i - 1 ] , neg_one)
175175 }
176176 } )
177177 . collect :: < Vec < _ > > ( ) ;
@@ -180,8 +180,8 @@ impl CircuitBuilderBits for CircuitBuilder<GoldilocksField, 2> {
180180 . into_iter ( )
181181 . enumerate ( )
182182 . map ( |( i, x) | match i {
183- 0 => self . add ( a[ 0 ] , x) ,
184- 1 => self . add ( a[ 1 ] , x) ,
183+ 0 => self . add ( a. 0 [ 0 ] , x) ,
184+ 1 => self . add ( a. 0 [ 1 ] , x) ,
185185 _ => x,
186186 } )
187187 . collect :: < Vec < _ > > ( ) ;
@@ -192,30 +192,24 @@ impl CircuitBuilderBits for CircuitBuilder<GoldilocksField, 2> {
192192 )
193193 } )
194194 . map ( |( _, v) | v)
195- . unwrap_or ( vec ! [ ] ) ;
195+ . unwrap_or ( ( vec ! [ ] , vec ! [ ] ) ) ;
196196 // Collect limbs, padding with 0s if necessary.
197- BigUInt320Target ( array:: from_fn ( |i| {
197+ let limbs : [ Target ; 10 ] = array:: from_fn ( |i| {
198198 if i < res_limbs. len ( ) {
199199 res_limbs[ i]
200200 } else {
201201 zero
202202 }
203- } ) )
204- }
205-
206- fn normalize_bigint (
207- & mut self ,
208- x : & BigUInt320Target ,
209- max_digit_bits : usize ,
210- max_num_carries : usize ,
211- ) -> BigUInt320Target {
212- let mut x = x. clone ( ) ;
213- for i in 0 ..max_num_carries {
214- let ( low, high) = self . split_low_high ( x. 0 [ i] , 32 , max_digit_bits) ;
215- x. 0 [ i] = low;
216- x. 0 [ i + 1 ] = self . add ( x. 0 [ i + 1 ] , high) ;
217- }
218- x
203+ } ) ;
204+ // Collect bits, padding with 0s if necessary.
205+ let bits: [ BoolTarget ; 320 ] = array:: from_fn ( |i| {
206+ if i < res_bits. len ( ) {
207+ res_bits[ i]
208+ } else {
209+ self . _false ( )
210+ }
211+ } ) ;
212+ BigUInt320Target { limbs, bits }
219213 }
220214
221215 fn split_32_bit ( & mut self , x : Target ) -> [ Target ; 2 ] {
@@ -226,44 +220,146 @@ impl CircuitBuilderBits for CircuitBuilder<GoldilocksField, 2> {
226220 [ low, high]
227221 }
228222
223+ fn split_low_high_with_bits (
224+ & mut self ,
225+ x : Target ,
226+ n_log : usize ,
227+ num_bits : usize ,
228+ ) -> ( ( Target , Vec < BoolTarget > ) , ( Target , Vec < BoolTarget > ) ) {
229+ let low = self . add_virtual_target ( ) ;
230+ let high = self . add_virtual_target ( ) ;
231+
232+ self . add_simple_generator ( LowHighGenerator {
233+ integer : x,
234+ n_log,
235+ low,
236+ high,
237+ } ) ;
238+
239+ let low_bits = self . split_le ( low, n_log) ;
240+ let high_bits = self . split_le ( high, num_bits - n_log) ;
241+
242+ let pow2 = self . constant ( F :: from_canonical_u64 ( 1 << n_log) ) ;
243+ let comp_x = self . mul_add ( high, pow2, low) ;
244+ self . connect ( x, comp_x) ;
245+
246+ ( ( low, low_bits) , ( high, high_bits) )
247+ }
248+
229249 fn constant_biguint320 ( & mut self , n : & BigUint ) -> BigUInt320Target {
230250 assert ! ( n. bits( ) <= 320 ) ;
231251 let digits = n. to_u32_digits ( ) ;
232- let targets = array:: from_fn ( |i| {
252+ let limbs : [ Target ; 10 ] = array:: from_fn ( |i| {
233253 let d = digits. get ( i) . copied ( ) . unwrap_or ( 0 ) ;
234254 self . constant ( GoldilocksField :: from_canonical_u32 ( d) )
235255 } ) ;
236- BigUInt320Target ( targets )
256+ self . biguint320_target_from_limbs ( & limbs )
237257 }
238258
239- fn add_virtual_biguint320_target ( & mut self ) -> BigUInt320Target {
240- let targets = self . add_virtual_target_arr ( ) ;
241- for t in targets {
242- self . range_check ( t, 32 ) ;
259+ fn biguint320_target_from_limbs ( & mut self , x : & [ Target ] ) -> BigUInt320Target {
260+ assert ! ( x. len( ) == 10 ) ;
261+ let limbs = array:: from_fn ( |i| x[ i] ) ;
262+ let bit_vec = biguint_limbs_to_bits ( self , x) ;
263+ BigUInt320Target {
264+ limbs,
265+ bits : array:: from_fn ( |i| bit_vec[ i] ) ,
243266 }
244- BigUInt320Target ( targets)
267+ }
268+
269+ fn add_virtual_biguint320_target ( & mut self ) -> BigUInt320Target {
270+ let limbs: [ Target ; 10 ] = self . add_virtual_target_arr ( ) ;
271+ self . biguint320_target_from_limbs ( & limbs)
245272 }
246273
247274 fn connect_biguint320 ( & mut self , x : & BigUInt320Target , y : & BigUInt320Target ) {
248275 for i in 0 ..10 {
249- self . connect ( x. 0 [ i] , y. 0 [ i] ) ;
276+ self . connect ( x. limbs [ i] , y. limbs [ i] ) ;
250277 }
251278 }
252279}
253280
254281/// Normalises the limbs of a biguint assuming no overflow in the
255- /// field.
282+ /// field. Returns the limbs together with their bit decomposition.
256283fn normalize_biguint_limbs (
257284 builder : & mut CircuitBuilder < F , D > ,
258285 x : & [ Target ] ,
259286 max_digit_bits : usize ,
260287 max_num_carries : usize ,
261- ) -> Vec < Target > {
288+ ) -> ( Vec < Target > , Vec < BoolTarget > ) {
262289 let mut x = x. to_vec ( ) ;
290+ let mut bits = Vec :: with_capacity ( 32 * ( max_num_carries + 1 ) ) ;
263291 for i in 0 ..max_num_carries {
264- let ( low, high) = builder. split_low_high ( x[ i] , 32 , max_digit_bits) ;
292+ let ( ( low, mut low_bits) , ( high, _) ) =
293+ builder. split_low_high_with_bits ( x[ i] , 32 , max_digit_bits) ;
265294 x[ i] = low;
266295 x[ i + 1 ] = builder. add ( x[ i + 1 ] , high) ;
296+ bits. append ( & mut low_bits) ;
297+ }
298+ let mut final_bits = builder. split_le ( x[ max_num_carries] , 32 ) ;
299+ bits. append ( & mut final_bits) ;
300+ ( x, bits)
301+ }
302+
303+ /// Converts biguint limbs to bits, checking that each limb is 32-bits
304+ /// long.
305+ fn biguint_limbs_to_bits ( builder : & mut CircuitBuilder < F , D > , limbs : & [ Target ] ) -> Vec < BoolTarget > {
306+ limbs
307+ . iter ( )
308+ . flat_map ( |t| builder. split_le ( * t, 32 ) )
309+ . collect ( )
310+ }
311+
312+ /*
313+ Copied from https://github.com/0xPolygonZero/plonky2/blob/82791c4809d6275682c34b926390ecdbdc2a5297/plonky2/src/gadgets/range_check.rs#L62
314+ */
315+
316+ #[ derive( Debug , Default ) ]
317+ pub struct LowHighGenerator {
318+ integer : Target ,
319+ n_log : usize ,
320+ low : Target ,
321+ high : Target ,
322+ }
323+
324+ impl < F : RichField + Extendable < D > , const D : usize > SimpleGenerator < F , D > for LowHighGenerator {
325+ fn id ( & self ) -> String {
326+ "LowHighGenerator" . to_string ( )
327+ }
328+
329+ fn dependencies ( & self ) -> Vec < Target > {
330+ vec ! [ self . integer]
331+ }
332+
333+ fn run_once (
334+ & self ,
335+ witness : & PartitionWitness < F > ,
336+ out_buffer : & mut GeneratedValues < F > ,
337+ ) -> anyhow:: Result < ( ) > {
338+ let integer_value = witness. get_target ( self . integer ) . to_canonical_u64 ( ) ;
339+ let low = integer_value & ( ( 1 << self . n_log ) - 1 ) ;
340+ let high = integer_value >> self . n_log ;
341+
342+ out_buffer. set_target ( self . low , F :: from_canonical_u64 ( low) ) ?;
343+ out_buffer. set_target ( self . high , F :: from_canonical_u64 ( high) )
344+ }
345+
346+ fn serialize ( & self , dst : & mut Vec < u8 > , _common_data : & CommonCircuitData < F , D > ) -> IoResult < ( ) > {
347+ dst. write_target ( self . integer ) ?;
348+ dst. write_usize ( self . n_log ) ?;
349+ dst. write_target ( self . low ) ?;
350+ dst. write_target ( self . high )
351+ }
352+
353+ fn deserialize ( src : & mut Buffer , _common_data : & CommonCircuitData < F , D > ) -> IoResult < Self > {
354+ let integer = src. read_target ( ) ?;
355+ let n_log = src. read_usize ( ) ?;
356+ let low = src. read_target ( ) ?;
357+ let high = src. read_target ( ) ?;
358+ Ok ( Self {
359+ integer,
360+ n_log,
361+ low,
362+ high,
363+ } )
267364 }
268- x
269365}
0 commit comments