@@ -6,7 +6,7 @@ use std::{
6
6
7
7
use itertools:: Itertools ;
8
8
use num_traits:: Zero ;
9
- use powdr_number:: { log2_exact, FieldElement } ;
9
+ use powdr_number:: { log2_exact, FieldElement , LargeInt } ;
10
10
11
11
use crate :: witgen:: jit:: effect:: Assertion ;
12
12
@@ -243,66 +243,93 @@ impl<T: FieldElement, V: Ord + Clone + Display> AffineSymbolicExpression<T, V> {
243
243
. coefficients
244
244
. iter ( )
245
245
. map ( |( var, coeff) | {
246
- let c = coeff. try_to_number ( ) ?;
246
+ let coeff = coeff. try_to_number ( ) ?;
247
247
let rc = self . range_constraints . get ( var) ?;
248
- Some ( ( var. clone ( ) , c, rc) )
248
+ let is_negative = !coeff. is_in_lower_half ( ) ;
249
+ let coeff_abs = if is_negative { -coeff } else { coeff } ;
250
+ // We could work with non-powers of two, but it would require
251
+ // division instead of shifts.
252
+ let exponent = log2_exact ( coeff_abs. to_arbitrary_integer ( ) ) ?;
253
+ // We negate here because we are solving
254
+ // c_1 * x_1 + c_2 * x_2 + ... + offset = 0,
255
+ // instead of
256
+ // c_1 * x_1 + c_2 * x_2 + ... = offset.
257
+ Some ( ( var. clone ( ) , rc, !is_negative, coeff_abs, exponent) )
249
258
} )
250
259
. collect :: < Option < Vec < _ > > > ( ) ;
251
260
let Some ( constrained_coefficients) = constrained_coefficients else {
252
261
return Ok ( ProcessResult :: empty ( ) ) ;
253
262
} ;
254
263
264
+ // If the offset is a known number, we gradually remove the
265
+ // components from this number.
266
+ let mut offset = self . offset . try_to_number ( ) ;
267
+ let mut concrete_assignments = vec ! [ ] ;
268
+
255
269
// Check if they are mutually exclusive and compute assignments.
256
270
let mut covered_bits: <T as FieldElement >:: Integer = 0 . into ( ) ;
257
271
let mut components = vec ! [ ] ;
258
- for ( variable, coeff, constraint) in constrained_coefficients {
259
- let is_negative = !coeff. is_in_lower_half ( ) ;
260
- let coeff_abs = if is_negative { -coeff } else { coeff } ;
261
- let Some ( exponent) = log2_exact ( coeff_abs. to_arbitrary_integer ( ) ) else {
262
- // We could work with non-powers of two, but it would require
263
- // division instead of shifts.
264
- return Ok ( ProcessResult :: empty ( ) ) ;
265
- } ;
272
+ for ( variable, constraint, is_negative, coeff_abs, exponent) in constrained_coefficients
273
+ . into_iter ( )
274
+ . sorted_by_key ( |( _, _, _, _, exponent) | * exponent)
275
+ {
266
276
let bit_mask = * constraint. multiple ( coeff_abs) . mask ( ) ;
267
277
if !( bit_mask & covered_bits) . is_zero ( ) {
268
278
// Overlapping range constraints.
269
279
return Ok ( ProcessResult :: empty ( ) ) ;
270
280
} else {
271
281
covered_bits |= bit_mask;
272
282
}
273
- components. push ( BitDecompositionComponent {
274
- variable,
275
- // We negate here because we are solving
276
- // c_1 * x_1 + c_2 * x_2 + ... + offset = 0,
277
- // instead of
278
- // c_1 * x_1 + c_2 * x_2 + ... = offset.
279
- is_negative : !is_negative,
280
- exponent : exponent as u64 ,
281
- bit_mask,
282
- } ) ;
283
+
284
+ // If the offset is a known number, we create concrete assignments and modify the offset.
285
+ // if it is not known, we return a BitDecomposition effect.
286
+ if let Some ( offset) = & mut offset {
287
+ let mut component = if is_negative { -* offset } else { * offset } . to_integer ( ) ;
288
+ if component > ( T :: modulus ( ) - 1 . into ( ) ) >> 1 {
289
+ // Convert a signed finite field element into two's complement.
290
+ // a regular subtraction would underflow, so we do this.
291
+ // We add the difference between negative numbers in the field
292
+ // and negative numbers in two's complement.
293
+ component += T :: Integer :: MAX - T :: modulus ( ) + 1 . into ( ) ;
294
+ } ;
295
+ component &= bit_mask;
296
+ concrete_assignments. push ( Effect :: Assignment (
297
+ variable. clone ( ) ,
298
+ T :: from ( component >> exponent) . into ( ) ,
299
+ ) ) ;
300
+ if is_negative {
301
+ * offset += T :: from ( component) ;
302
+ } else {
303
+ * offset -= T :: from ( component) ;
304
+ }
305
+ } else {
306
+ components. push ( BitDecompositionComponent {
307
+ variable,
308
+ is_negative,
309
+ exponent : exponent as u64 ,
310
+ bit_mask,
311
+ } ) ;
312
+ }
283
313
}
284
314
285
315
if covered_bits >= T :: modulus ( ) {
286
316
return Ok ( ProcessResult :: empty ( ) ) ;
287
317
}
288
318
289
- if !components. iter ( ) . any ( |c| c. is_negative ) {
290
- // If all coefficients are positive and the offset is known, we can check
291
- // that all bits are covered. If not, then there is no way to extract
292
- // the components and thus we have a conflict.
293
- if let Some ( offset) = self . offset . try_to_number ( ) {
294
- if offset. to_integer ( ) & !covered_bits != 0 . into ( ) {
295
- return Err ( Error :: ConflictingRangeConstraints ) ;
296
- }
319
+ if let Some ( offset) = offset {
320
+ if offset != 0 . into ( ) {
321
+ return Err ( Error :: ConstraintUnsatisfiable ) ;
297
322
}
323
+ assert_eq ! ( concrete_assignments. len( ) , self . coefficients. len( ) ) ;
324
+ Ok ( ProcessResult :: complete ( concrete_assignments) )
325
+ } else {
326
+ Ok ( ProcessResult :: complete ( vec ! [ Effect :: BitDecomposition (
327
+ BitDecomposition {
328
+ value: self . offset. clone( ) ,
329
+ components,
330
+ } ,
331
+ ) ] ) )
298
332
}
299
-
300
- Ok ( ProcessResult :: complete ( vec ! [ Effect :: BitDecomposition (
301
- BitDecomposition {
302
- value: self . offset. clone( ) ,
303
- components,
304
- } ,
305
- ) ] ) )
306
333
}
307
334
308
335
fn transfer_constraints ( & self ) -> Option < Effect < T , V > > {
0 commit comments