@@ -260,10 +260,10 @@ impl<'a, T: FieldElement> Machine<'a, T> for FixedLookup<'a, T> {
260
260
& mut self ,
261
261
identity_id : u64 ,
262
262
known_arguments : & BitVec ,
263
- _range_constraints : & [ RangeConstraint < T > ] ,
264
- ) -> bool {
263
+ range_constraints : & [ RangeConstraint < T > ] ,
264
+ ) -> Option < Vec < RangeConstraint < T > > > {
265
265
if !Self :: is_responsible ( & self . connections [ & identity_id] ) {
266
- return false ;
266
+ return None ;
267
267
}
268
268
let index = self
269
269
. indices
@@ -274,8 +274,72 @@ impl<'a, T: FieldElement> Machine<'a, T> for FixedLookup<'a, T> {
274
274
. or_insert_with_key ( |application| {
275
275
create_index ( self . fixed_data , application, & self . connections )
276
276
} ) ;
277
- // Check that if there is a match, it is unique.
278
- index. values ( ) . all ( |value| value. 0 . is_some ( ) )
277
+ let input_range_constraints = known_arguments
278
+ . iter ( )
279
+ . zip_eq ( range_constraints)
280
+ . filter_map ( |( is_known, range_constraint) | is_known. then_some ( range_constraint. clone ( ) ) )
281
+ . collect_vec ( ) ;
282
+
283
+ // Now only consider the index entries that match the input range constraints,
284
+ // see that the result is unique and determine new output range constraints.
285
+ let values_matching_input_constraints = index
286
+ . iter ( )
287
+ . filter ( |( key, _) | matches_range_constraint ( key, & input_range_constraints) )
288
+ . map ( |( _, value) | {
289
+ let ( _, values) = value. get ( ) ?;
290
+ Some ( values)
291
+ } ) ;
292
+ let mut new_range_constraints: Option < Vec < ( T , T , T :: Integer ) > > = None ;
293
+ for values in values_matching_input_constraints {
294
+ // If any value is None, it means the lookup does not have a unique answer,
295
+ // and thus we cannot process the call.
296
+ let values = values?;
297
+ new_range_constraints = Some ( match new_range_constraints {
298
+ // First item, turn each value into (min, max, mask).
299
+ None => values
300
+ . iter ( )
301
+ . map ( |v| ( * v, * v, v. to_integer ( ) ) )
302
+ . collect_vec ( ) ,
303
+ // Reduce range constraint by disjunction.
304
+ Some ( mut acc) => {
305
+ for ( ( min, max, mask) , v) in acc. iter_mut ( ) . zip_eq ( values) {
306
+ * min = ( * min) . min ( * v) ;
307
+ * max = ( * max) . max ( * v) ;
308
+ * mask |= v. to_integer ( ) ;
309
+ }
310
+ acc
311
+ }
312
+ } )
313
+ }
314
+ Some ( match new_range_constraints {
315
+ None => {
316
+ // The iterator was empty, i.e. there are no inputs in the index matching the
317
+ // range constraints.
318
+ // This means that every call like this will lead to a fatal error, but there is
319
+ // enough information in the inputs to hanlde the call. Unfortunately, there is
320
+ // no way to signal this in the return type, yet.
321
+ // TODO(#2324): change this.
322
+ // We just return the input range constraints to signal "everything allright".
323
+ range_constraints. to_vec ( )
324
+ }
325
+ Some ( new_output_range_constraints) => {
326
+ let mut new_output_range_constraints = new_output_range_constraints. into_iter ( ) ;
327
+ known_arguments
328
+ . iter ( )
329
+ . enumerate ( )
330
+ . map ( |( i, is_known) | {
331
+ if is_known {
332
+ // Just copy the input range constraint.
333
+ range_constraints[ i] . clone ( )
334
+ } else {
335
+ let ( min, max, mask) = new_output_range_constraints. next ( ) . unwrap ( ) ;
336
+ RangeConstraint :: from_range ( min, max)
337
+ . conjunction ( & RangeConstraint :: from_mask ( mask) )
338
+ }
339
+ } )
340
+ . collect ( )
341
+ }
342
+ } )
279
343
}
280
344
281
345
fn process_plookup < Q : crate :: witgen:: QueryCallback < T > > (
@@ -398,3 +462,14 @@ impl<'a, T: FieldElement> RangeConstraintSet<AlgebraicVariable<'a>, T>
398
462
}
399
463
}
400
464
}
465
+
466
+ /// Checks that an array of values satisfies a set of range constraints.
467
+ fn matches_range_constraint < T : FieldElement > (
468
+ values : & [ T ] ,
469
+ range_constraints : & [ RangeConstraint < T > ] ,
470
+ ) -> bool {
471
+ values
472
+ . iter ( )
473
+ . zip_eq ( range_constraints)
474
+ . all ( |( value, range_constraint) | range_constraint. allows_value ( * value) )
475
+ }
0 commit comments