1use std::{
2 array,
3 borrow::{Borrow, BorrowMut},
4};
5
6use num_bigint::BigUint;
7use num_integer::Integer;
8use openvm_circuit::{
9 arch::*,
10 system::memory::{online::TracingMemory, MemoryAuxColsFactory},
11};
12use openvm_circuit_primitives::{
13 bitwise_op_lookup::{BitwiseOperationLookupBus, SharedBitwiseOperationLookupChip},
14 range_tuple::{RangeTupleCheckerBus, SharedRangeTupleCheckerChip},
15 utils::{not, select},
16 AlignedBytesBorrow, ColumnsAir, StructReflection, StructReflectionHelper,
17};
18use openvm_circuit_primitives_derive::AlignedBorrow;
19use openvm_instructions::{instruction::Instruction, program::DEFAULT_PC_STEP, LocalOpcode};
20use openvm_rv32im_transpiler::DivRemOpcode;
21use openvm_stark_backend::{
22 interaction::InteractionBuilder,
23 p3_air::{AirBuilder, BaseAir},
24 p3_field::{Field, PrimeCharacteristicRing, PrimeField32},
25 BaseAirWithPublicValues,
26};
27use strum::IntoEnumIterator;
28
29#[repr(C)]
30#[derive(AlignedBorrow, StructReflection)]
31pub struct DivRemCoreCols<T, const NUM_LIMBS: usize, const LIMB_BITS: usize> {
32 pub b: [T; NUM_LIMBS],
34 pub c: [T; NUM_LIMBS],
35 pub q: [T; NUM_LIMBS],
36 pub r: [T; NUM_LIMBS],
37
38 pub zero_divisor: T,
40 pub r_zero: T,
41
42 pub b_sign: T,
45 pub c_sign: T,
46 pub q_sign: T,
47 pub sign_xor: T,
48
49 pub c_sum_inv: T,
51 pub r_sum_inv: T,
53
54 pub r_prime: [T; NUM_LIMBS],
58 pub r_inv: [T; NUM_LIMBS],
59 pub lt_marker: [T; NUM_LIMBS],
60 pub lt_diff: T,
61
62 pub opcode_div_flag: T,
64 pub opcode_divu_flag: T,
65 pub opcode_rem_flag: T,
66 pub opcode_remu_flag: T,
67}
68
69#[derive(Copy, Clone, Debug, derive_new::new, ColumnsAir)]
70#[columns_via(DivRemCoreCols<u8, NUM_LIMBS, LIMB_BITS>)]
71pub struct DivRemCoreAir<const NUM_LIMBS: usize, const LIMB_BITS: usize> {
72 pub bitwise_lookup_bus: BitwiseOperationLookupBus,
73 pub range_tuple_bus: RangeTupleCheckerBus<2>,
74 offset: usize,
75}
76
77impl<F: Field, const NUM_LIMBS: usize, const LIMB_BITS: usize> BaseAir<F>
78 for DivRemCoreAir<NUM_LIMBS, LIMB_BITS>
79{
80 fn width(&self) -> usize {
81 DivRemCoreCols::<F, NUM_LIMBS, LIMB_BITS>::width()
82 }
83}
84impl<F: Field, const NUM_LIMBS: usize, const LIMB_BITS: usize> BaseAirWithPublicValues<F>
85 for DivRemCoreAir<NUM_LIMBS, LIMB_BITS>
86{
87}
88
89impl<AB, I, const NUM_LIMBS: usize, const LIMB_BITS: usize> VmCoreAir<AB, I>
90 for DivRemCoreAir<NUM_LIMBS, LIMB_BITS>
91where
92 AB: InteractionBuilder,
93 I: VmAdapterInterface<AB::Expr>,
94 I::Reads: From<[[AB::Expr; NUM_LIMBS]; 2]>,
95 I::Writes: From<[[AB::Expr; NUM_LIMBS]; 1]>,
96 I::ProcessedInstruction: From<MinimalInstruction<AB::Expr>>,
97{
98 fn eval(
99 &self,
100 builder: &mut AB,
101 local_core: &[AB::Var],
102 _from_pc: AB::Var,
103 ) -> AdapterAirContext<AB::Expr, I> {
104 let cols: &DivRemCoreCols<_, NUM_LIMBS, LIMB_BITS> = local_core.borrow();
105 let flags = [
106 cols.opcode_div_flag,
107 cols.opcode_divu_flag,
108 cols.opcode_rem_flag,
109 cols.opcode_remu_flag,
110 ];
111
112 let is_valid = flags.iter().fold(AB::Expr::ZERO, |acc, &flag| {
113 builder.assert_bool(flag);
114 acc + flag.into()
115 });
116 builder.assert_bool(is_valid.clone());
117
118 let b = &cols.b;
119 let c = &cols.c;
120 let q = &cols.q;
121 let r = &cols.r;
122
123 let b_ext = cols.b_sign * AB::F::from_u32((1 << LIMB_BITS) - 1);
126 let c_ext = cols.c_sign * AB::F::from_u32((1 << LIMB_BITS) - 1);
127 let carry_divide = AB::F::from_u32(1 << LIMB_BITS).inverse();
128 let mut carry: [AB::Expr; NUM_LIMBS] = array::from_fn(|_| AB::Expr::ZERO);
129
130 for i in 0..NUM_LIMBS {
131 let expected_limb = if i == 0 {
132 AB::Expr::ZERO
133 } else {
134 carry[i - 1].clone()
135 } + (0..=i).fold(r[i].into(), |ac, k| ac + (c[k] * q[i - k]));
136 carry[i] = (expected_limb - b[i]) * carry_divide;
137 }
138
139 for (q, carry) in q.iter().zip(carry.iter()) {
140 self.range_tuple_bus
141 .send(vec![(*q).into(), carry.clone()])
142 .eval(builder, is_valid.clone());
143 }
144
145 let q_ext = cols.q_sign * AB::F::from_u32((1 << LIMB_BITS) - 1);
148 let mut carry_ext: [AB::Expr; NUM_LIMBS] = array::from_fn(|_| AB::Expr::ZERO);
149
150 for j in 0..NUM_LIMBS {
151 let expected_limb = if j == 0 {
152 carry[NUM_LIMBS - 1].clone()
153 } else {
154 carry_ext[j - 1].clone()
155 } + ((j + 1)..NUM_LIMBS)
156 .fold(AB::Expr::ZERO, |acc, k| acc + (c[k] * q[NUM_LIMBS + j - k]))
157 + (0..(j + 1)).fold(AB::Expr::ZERO, |acc, k| {
158 acc + (c[k] * q_ext.clone()) + (q[k] * c_ext.clone())
159 })
160 + (AB::Expr::ONE - cols.r_zero) * b_ext.clone();
161 carry_ext[j] = (expected_limb - b_ext.clone()) * carry_divide;
169 }
170
171 for (r, carry) in r.iter().zip(carry_ext.iter()) {
172 self.range_tuple_bus
173 .send(vec![(*r).into(), carry.clone()])
174 .eval(builder, is_valid.clone());
175 }
176
177 let special_case = cols.zero_divisor + cols.r_zero;
180 builder.assert_bool(special_case.clone());
181
182 builder.assert_bool(cols.zero_divisor);
184 let mut when_zero_divisor = builder.when(cols.zero_divisor);
185 for i in 0..NUM_LIMBS {
186 when_zero_divisor.assert_zero(c[i]);
187 when_zero_divisor.assert_eq(q[i], AB::F::from_u32((1 << LIMB_BITS) - 1));
188 }
189 let c_sum = c.iter().fold(AB::Expr::ZERO, |acc, c| acc + *c);
194 let valid_and_not_zero_divisor = is_valid.clone() - cols.zero_divisor;
195 builder.assert_bool(valid_and_not_zero_divisor.clone());
196 builder
197 .when(valid_and_not_zero_divisor)
198 .assert_one(c_sum * cols.c_sum_inv);
199
200 builder.assert_bool(cols.r_zero);
202 r.iter()
203 .for_each(|r_i| builder.when(cols.r_zero).assert_zero(*r_i));
204 let r_sum = r.iter().fold(AB::Expr::ZERO, |acc, r| acc + *r);
207 let valid_and_not_special_case = is_valid.clone() - special_case.clone();
208 builder.assert_bool(valid_and_not_special_case.clone());
209 builder
210 .when(valid_and_not_special_case)
211 .assert_one(r_sum * cols.r_sum_inv);
212
213 let signed = cols.opcode_div_flag + cols.opcode_rem_flag;
217
218 builder.assert_bool(cols.b_sign);
219 builder.assert_bool(cols.c_sign);
220 builder
221 .when(not::<AB::Expr>(signed.clone()))
222 .assert_zero(cols.b_sign);
223 builder
224 .when(not::<AB::Expr>(signed.clone()))
225 .assert_zero(cols.c_sign);
226 builder.assert_eq(
227 cols.b_sign + cols.c_sign - AB::Expr::from_u32(2) * cols.b_sign * cols.c_sign,
228 cols.sign_xor,
229 );
230
231 let nonzero_q = q.iter().fold(AB::Expr::ZERO, |acc, q| acc + *q);
240 builder.assert_bool(cols.q_sign);
241 builder
242 .when(nonzero_q)
243 .when(not(cols.zero_divisor))
244 .assert_eq(cols.q_sign, cols.sign_xor);
245 builder
246 .when_ne(cols.q_sign, cols.sign_xor)
247 .when(not(cols.zero_divisor))
248 .assert_zero(cols.q_sign);
249
250 let sign_mask = AB::F::from_u32(1 << (LIMB_BITS - 1));
252 self.bitwise_lookup_bus
253 .send_range(
254 AB::Expr::from_u32(2) * (b[NUM_LIMBS - 1] - cols.b_sign * sign_mask),
255 AB::Expr::from_u32(2) * (c[NUM_LIMBS - 1] - cols.c_sign * sign_mask),
256 )
257 .eval(builder, signed.clone());
258
259 let r_p = &cols.r_prime;
266 let mut carry_lt: [AB::Expr; NUM_LIMBS] = array::from_fn(|_| AB::Expr::ZERO);
267
268 for i in 0..NUM_LIMBS {
269 builder.when(not(cols.sign_xor)).assert_eq(r[i], r_p[i]);
271
272 let last_carry = if i > 0 {
278 carry_lt[i - 1].clone()
279 } else {
280 AB::Expr::ZERO
281 };
282 carry_lt[i] = (last_carry.clone() + r[i] + r_p[i]) * carry_divide;
283 builder.when(cols.sign_xor).assert_zero(
284 (carry_lt[i].clone() - last_carry) * (carry_lt[i].clone() - AB::Expr::ONE),
285 );
286 builder
287 .when(cols.sign_xor)
288 .assert_one((r_p[i] - AB::F::from_u32(1 << LIMB_BITS)) * cols.r_inv[i]);
289 builder
290 .when(cols.sign_xor)
291 .when(not::<AB::Expr>(carry_lt[i].clone()))
292 .assert_zero(r_p[i]);
293 }
294
295 let marker = &cols.lt_marker;
296 let mut prefix_sum = special_case.clone();
297
298 for i in (0..NUM_LIMBS).rev() {
299 let diff = r_p[i] * (AB::Expr::from_u8(2) * cols.c_sign - AB::Expr::ONE)
300 + c[i] * (AB::Expr::ONE - AB::Expr::from_u8(2) * cols.c_sign);
301 prefix_sum += marker[i].into();
302 builder.assert_bool(marker[i]);
303 builder.assert_zero(not::<AB::Expr>(prefix_sum.clone()) * diff.clone());
304 builder.when(marker[i]).assert_eq(cols.lt_diff, diff);
305 }
306 builder.when(is_valid.clone()).assert_one(prefix_sum);
312 self.bitwise_lookup_bus
314 .send_range(cols.lt_diff - AB::Expr::ONE, AB::F::ZERO)
315 .eval(builder, is_valid.clone() - special_case);
316
317 let expected_opcode = flags.iter().zip(DivRemOpcode::iter()).fold(
319 AB::Expr::ZERO,
320 |acc, (flag, local_opcode)| {
321 acc + (*flag).into() * AB::Expr::from_u8(local_opcode as u8)
322 },
323 ) + AB::Expr::from_usize(self.offset);
324
325 let is_div = cols.opcode_div_flag + cols.opcode_divu_flag;
326 let a = array::from_fn(|i| select(is_div.clone(), q[i], r[i]));
327
328 AdapterAirContext {
329 to_pc: None,
330 reads: [cols.b.map(Into::into), cols.c.map(Into::into)].into(),
331 writes: [a.map(Into::into)].into(),
332 instruction: MinimalInstruction {
333 is_valid,
334 opcode: expected_opcode,
335 }
336 .into(),
337 }
338 }
339
340 fn start_offset(&self) -> usize {
341 self.offset
342 }
343}
344
345#[derive(Debug, Eq, PartialEq)]
346#[repr(u8)]
347pub(super) enum DivRemCoreSpecialCase {
348 None,
349 ZeroDivisor,
350 SignedOverflow,
351}
352
353#[repr(C)]
354#[derive(AlignedBytesBorrow, Debug)]
355pub struct DivRemCoreRecord<const NUM_LIMBS: usize> {
356 pub b: [u8; NUM_LIMBS],
357 pub c: [u8; NUM_LIMBS],
358 pub local_opcode: u8,
359}
360
361#[derive(Clone, Copy, derive_new::new)]
362pub struct DivRemExecutor<A, const NUM_LIMBS: usize, const LIMB_BITS: usize> {
363 adapter: A,
364 pub offset: usize,
365}
366
367pub struct DivRemFiller<A, const NUM_LIMBS: usize, const LIMB_BITS: usize> {
368 adapter: A,
369 pub offset: usize,
370 pub bitwise_lookup_chip: SharedBitwiseOperationLookupChip<LIMB_BITS>,
371 pub range_tuple_chip: SharedRangeTupleCheckerChip<2>,
372}
373
374impl<A, const NUM_LIMBS: usize, const LIMB_BITS: usize> DivRemFiller<A, NUM_LIMBS, LIMB_BITS> {
375 pub fn new(
376 adapter: A,
377 bitwise_lookup_chip: SharedBitwiseOperationLookupChip<LIMB_BITS>,
378 range_tuple_chip: SharedRangeTupleCheckerChip<2>,
379 offset: usize,
380 ) -> Self {
381 debug_assert!(
385 range_tuple_chip.sizes()[0] == 1 << LIMB_BITS,
386 "First element of RangeTupleChecker must have size {}",
387 1 << LIMB_BITS
388 );
389 debug_assert!(
390 range_tuple_chip.sizes()[1] >= (1 << LIMB_BITS) * 2 * NUM_LIMBS as u32,
391 "Second element of RangeTupleChecker must have size of at least {}",
392 (1 << LIMB_BITS) * 2 * NUM_LIMBS as u32
393 );
394
395 Self {
396 adapter,
397 offset,
398 bitwise_lookup_chip,
399 range_tuple_chip,
400 }
401 }
402}
403
404impl<F, A, RA, const NUM_LIMBS: usize, const LIMB_BITS: usize> PreflightExecutor<F, RA>
405 for DivRemExecutor<A, NUM_LIMBS, LIMB_BITS>
406where
407 F: PrimeField32,
408 A: 'static
409 + AdapterTraceExecutor<
410 F,
411 ReadData: Into<[[u8; NUM_LIMBS]; 2]>,
412 WriteData: From<[[u8; NUM_LIMBS]; 1]>,
413 >,
414 for<'buf> RA: RecordArena<
415 'buf,
416 EmptyAdapterCoreLayout<F, A>,
417 (A::RecordMut<'buf>, &'buf mut DivRemCoreRecord<NUM_LIMBS>),
418 >,
419{
420 fn get_opcode_name(&self, opcode: usize) -> String {
421 format!("{:?}", DivRemOpcode::from_usize(opcode - self.offset))
422 }
423
424 fn execute(
425 &self,
426 state: VmStateMut<F, TracingMemory, RA>,
427 instruction: &Instruction<F>,
428 ) -> Result<(), ExecutionError> {
429 let Instruction { opcode, .. } = instruction;
430
431 let (mut adapter_record, core_record) = state.ctx.alloc(EmptyAdapterCoreLayout::new());
432
433 A::start(*state.pc, state.memory, &mut adapter_record);
434
435 core_record.local_opcode = opcode.local_opcode_idx(self.offset) as u8;
436
437 let is_signed = core_record.local_opcode == DivRemOpcode::DIV as u8
438 || core_record.local_opcode == DivRemOpcode::REM as u8;
439 let is_div = core_record.local_opcode == DivRemOpcode::DIV as u8
440 || core_record.local_opcode == DivRemOpcode::DIVU as u8;
441
442 [core_record.b, core_record.c] = self
443 .adapter
444 .read(state.memory, instruction, &mut adapter_record)
445 .into();
446
447 let b = core_record.b.map(u32::from);
448 let c = core_record.c.map(u32::from);
449 let (q, r, _, _, _, _) = run_divrem::<NUM_LIMBS, LIMB_BITS>(is_signed, &b, &c);
450
451 let rd = if is_div {
452 q.map(|x| x as u8)
453 } else {
454 r.map(|x| x as u8)
455 };
456
457 self.adapter
458 .write(state.memory, instruction, [rd].into(), &mut adapter_record);
459
460 *state.pc = state.pc.wrapping_add(DEFAULT_PC_STEP);
461
462 Ok(())
463 }
464}
465
466impl<F, A, const NUM_LIMBS: usize, const LIMB_BITS: usize> TraceFiller<F>
467 for DivRemFiller<A, NUM_LIMBS, LIMB_BITS>
468where
469 F: PrimeField32,
470 A: 'static + AdapterTraceFiller<F>,
471{
472 fn fill_trace_row(&self, mem_helper: &MemoryAuxColsFactory<F>, row_slice: &mut [F]) {
473 let (adapter_row, mut core_row) = unsafe { row_slice.split_at_mut_unchecked(A::WIDTH) };
476 self.adapter.fill_trace_row(mem_helper, adapter_row);
477 let record: &DivRemCoreRecord<NUM_LIMBS> =
480 unsafe { get_record_from_slice(&mut core_row, ()) };
481 let core_row: &mut DivRemCoreCols<F, NUM_LIMBS, LIMB_BITS> = core_row.borrow_mut();
482
483 let opcode = DivRemOpcode::from_usize(record.local_opcode as usize);
484 let is_signed = opcode == DivRemOpcode::DIV || opcode == DivRemOpcode::REM;
485
486 let (q, r, b_sign, c_sign, q_sign, case) = run_divrem::<NUM_LIMBS, LIMB_BITS>(
487 is_signed,
488 &record.b.map(u32::from),
489 &record.c.map(u32::from),
490 );
491
492 let carries = run_mul_carries::<NUM_LIMBS, LIMB_BITS>(
493 is_signed,
494 &record.c.map(u32::from),
495 &q,
496 &r,
497 q_sign,
498 );
499 for i in 0..NUM_LIMBS {
500 self.range_tuple_chip.add_count(&[q[i], carries[i]]);
501 self.range_tuple_chip
502 .add_count(&[r[i], carries[i + NUM_LIMBS]]);
503 }
504
505 let sign_xor = b_sign ^ c_sign;
506 let r_prime = if sign_xor {
507 negate::<NUM_LIMBS, LIMB_BITS>(&r)
508 } else {
509 r
510 };
511 let r_zero = r.iter().all(|&v| v == 0) && case != DivRemCoreSpecialCase::ZeroDivisor;
512
513 if is_signed {
514 let b_sign_mask = if b_sign { 1 << (LIMB_BITS - 1) } else { 0 };
515 let c_sign_mask = if c_sign { 1 << (LIMB_BITS - 1) } else { 0 };
516 self.bitwise_lookup_chip.request_range(
517 (record.b[NUM_LIMBS - 1] as u32 - b_sign_mask) << 1,
518 (record.c[NUM_LIMBS - 1] as u32 - c_sign_mask) << 1,
519 );
520 }
521
522 core_row.opcode_remu_flag = F::from_bool(opcode == DivRemOpcode::REMU);
524 core_row.opcode_rem_flag = F::from_bool(opcode == DivRemOpcode::REM);
525 core_row.opcode_divu_flag = F::from_bool(opcode == DivRemOpcode::DIVU);
526 core_row.opcode_div_flag = F::from_bool(opcode == DivRemOpcode::DIV);
527
528 core_row.lt_diff = F::ZERO;
529 core_row.lt_marker = [F::ZERO; NUM_LIMBS];
530 if case == DivRemCoreSpecialCase::None && !r_zero {
531 let idx = run_sltu_diff_idx(&record.c.map(u32::from), &r_prime, c_sign);
532 let val = if c_sign {
533 r_prime[idx] - record.c[idx] as u32
534 } else {
535 record.c[idx] as u32 - r_prime[idx]
536 };
537 self.bitwise_lookup_chip.request_range(val - 1, 0);
538 core_row.lt_diff = F::from_u32(val);
539 core_row.lt_marker[idx] = F::ONE;
540 }
541
542 let r_prime_f = r_prime.map(F::from_u32);
543 core_row.r_inv = r_prime_f.map(|r| (r - F::from_u32(256)).inverse());
544 core_row.r_prime = r_prime_f;
545
546 let r_sum_f = r.iter().fold(F::ZERO, |acc, r| acc + F::from_u32(*r));
547 core_row.r_sum_inv = r_sum_f.try_inverse().unwrap_or(F::ZERO);
548
549 let c_sum_f = F::from_u32(record.c.iter().fold(0, |acc, c| acc + *c as u32));
550 core_row.c_sum_inv = c_sum_f.try_inverse().unwrap_or(F::ZERO);
551
552 core_row.sign_xor = F::from_bool(sign_xor);
553 core_row.q_sign = F::from_bool(q_sign);
554 core_row.c_sign = F::from_bool(c_sign);
555 core_row.b_sign = F::from_bool(b_sign);
556
557 core_row.r_zero = F::from_bool(r_zero);
558 core_row.zero_divisor = F::from_bool(case == DivRemCoreSpecialCase::ZeroDivisor);
559
560 core_row.r = r.map(F::from_u32);
561 core_row.q = q.map(F::from_u32);
562 core_row.c = record.c.map(F::from_u8);
563 core_row.b = record.b.map(F::from_u8);
564 }
565}
566
567#[inline(always)]
570pub(super) fn run_divrem<const NUM_LIMBS: usize, const LIMB_BITS: usize>(
571 signed: bool,
572 x: &[u32; NUM_LIMBS],
573 y: &[u32; NUM_LIMBS],
574) -> (
575 [u32; NUM_LIMBS],
576 [u32; NUM_LIMBS],
577 bool,
578 bool,
579 bool,
580 DivRemCoreSpecialCase,
581) {
582 let x_sign = signed && (x[NUM_LIMBS - 1] >> (LIMB_BITS - 1) == 1);
583 let y_sign = signed && (y[NUM_LIMBS - 1] >> (LIMB_BITS - 1) == 1);
584 let max_limb = (1 << LIMB_BITS) - 1;
585
586 let zero_divisor = y.iter().all(|val| *val == 0);
587 let overflow = x[NUM_LIMBS - 1] == 1 << (LIMB_BITS - 1)
588 && x[..(NUM_LIMBS - 1)].iter().all(|val| *val == 0)
589 && y.iter().all(|val| *val == max_limb)
590 && x_sign
591 && y_sign;
592
593 if zero_divisor {
594 return (
595 [max_limb; NUM_LIMBS],
596 *x,
597 x_sign,
598 y_sign,
599 signed,
600 DivRemCoreSpecialCase::ZeroDivisor,
601 );
602 } else if overflow {
603 return (
604 *x,
605 [0; NUM_LIMBS],
606 x_sign,
607 y_sign,
608 false,
609 DivRemCoreSpecialCase::SignedOverflow,
610 );
611 }
612
613 let x_abs = if x_sign {
614 negate::<NUM_LIMBS, LIMB_BITS>(x)
615 } else {
616 *x
617 };
618 let y_abs = if y_sign {
619 negate::<NUM_LIMBS, LIMB_BITS>(y)
620 } else {
621 *y
622 };
623
624 let x_big = limbs_to_biguint::<NUM_LIMBS, LIMB_BITS>(&x_abs);
625 let y_big = limbs_to_biguint::<NUM_LIMBS, LIMB_BITS>(&y_abs);
626 let q_big = x_big.clone() / y_big.clone();
627 let r_big = x_big.clone() % y_big.clone();
628
629 let q = if x_sign ^ y_sign {
630 negate::<NUM_LIMBS, LIMB_BITS>(&biguint_to_limbs::<NUM_LIMBS, LIMB_BITS>(&q_big))
631 } else {
632 biguint_to_limbs::<NUM_LIMBS, LIMB_BITS>(&q_big)
633 };
634 let q_sign = signed && (q[NUM_LIMBS - 1] >> (LIMB_BITS - 1) == 1);
635
636 let r = if x_sign {
638 negate::<NUM_LIMBS, LIMB_BITS>(&biguint_to_limbs::<NUM_LIMBS, LIMB_BITS>(&r_big))
639 } else {
640 biguint_to_limbs::<NUM_LIMBS, LIMB_BITS>(&r_big)
641 };
642
643 (q, r, x_sign, y_sign, q_sign, DivRemCoreSpecialCase::None)
644}
645
646#[inline(always)]
647pub(super) fn run_sltu_diff_idx<const NUM_LIMBS: usize>(
648 x: &[u32; NUM_LIMBS],
649 y: &[u32; NUM_LIMBS],
650 cmp: bool,
651) -> usize {
652 for i in (0..NUM_LIMBS).rev() {
653 if x[i] != y[i] {
654 assert!((x[i] < y[i]) == cmp);
655 return i;
656 }
657 }
658 assert!(!cmp);
659 NUM_LIMBS
660}
661
662#[inline(always)]
664pub(super) fn run_mul_carries<const NUM_LIMBS: usize, const LIMB_BITS: usize>(
665 signed: bool,
666 d: &[u32; NUM_LIMBS],
667 q: &[u32; NUM_LIMBS],
668 r: &[u32; NUM_LIMBS],
669 q_sign: bool,
670) -> Vec<u32> {
671 let mut carry = vec![0u32; 2 * NUM_LIMBS];
672 for i in 0..NUM_LIMBS {
673 let mut val = r[i] + if i > 0 { carry[i - 1] } else { 0 };
674 for j in 0..=i {
675 val += d[j] * q[i - j];
676 }
677 carry[i] = val >> LIMB_BITS;
678 }
679
680 let q_ext = if q_sign && signed {
681 (1 << LIMB_BITS) - 1
682 } else {
683 0
684 };
685 let d_ext =
686 (d[NUM_LIMBS - 1] >> (LIMB_BITS - 1)) * if signed { (1 << LIMB_BITS) - 1 } else { 0 };
687 let r_ext =
688 (r[NUM_LIMBS - 1] >> (LIMB_BITS - 1)) * if signed { (1 << LIMB_BITS) - 1 } else { 0 };
689 let mut d_prefix = 0;
690 let mut q_prefix = 0;
691
692 for i in 0..NUM_LIMBS {
693 d_prefix += d[i];
694 q_prefix += q[i];
695 let mut val = carry[NUM_LIMBS + i - 1] + d_prefix * q_ext + q_prefix * d_ext + r_ext;
696 for j in (i + 1)..NUM_LIMBS {
697 val += d[j] * q[NUM_LIMBS + i - j];
698 }
699 carry[NUM_LIMBS + i] = val >> LIMB_BITS;
700 }
701 carry
702}
703
704#[inline(always)]
705fn limbs_to_biguint<const NUM_LIMBS: usize, const LIMB_BITS: usize>(
706 x: &[u32; NUM_LIMBS],
707) -> BigUint {
708 let base = BigUint::new(vec![1 << LIMB_BITS]);
709 let mut res = BigUint::new(vec![0]);
710 for val in x.iter().rev() {
711 res *= base.clone();
712 res += BigUint::new(vec![*val]);
713 }
714 res
715}
716
717#[inline(always)]
718fn biguint_to_limbs<const NUM_LIMBS: usize, const LIMB_BITS: usize>(
719 x: &BigUint,
720) -> [u32; NUM_LIMBS] {
721 let mut res = [0; NUM_LIMBS];
722 let mut x = x.clone();
723 let base = BigUint::from(1u32 << LIMB_BITS);
724 for limb in res.iter_mut() {
725 let (quot, rem) = x.div_rem(&base);
726 *limb = rem.iter_u32_digits().next().unwrap_or(0);
727 x = quot;
728 }
729 debug_assert_eq!(x, BigUint::from(0u32));
730 res
731}
732
733#[inline(always)]
734fn negate<const NUM_LIMBS: usize, const LIMB_BITS: usize>(
735 x: &[u32; NUM_LIMBS],
736) -> [u32; NUM_LIMBS] {
737 let mut carry = 1;
738 array::from_fn(|i| {
739 let val = (1 << LIMB_BITS) + carry - 1 - x[i];
740 carry = val >> LIMB_BITS;
741 val % (1 << LIMB_BITS)
742 })
743}