use alloc::{vec, vec::Vec};
use miden_core::Felt;
use miden_processor::{
ProcessorState,
advice::AdviceMutation,
event::{EventError, EventName},
};
pub const U256_DIV_EVENT_NAME: EventName = EventName::new("miden::core::math::u256::u256_div");
pub fn handle_u256_div(process: &ProcessorState) -> Result<Vec<AdviceMutation>, EventError> {
let divisor = read_u256_from_stack(process, 1, "divisor")?;
if divisor == (0, 0) {
return Err(U256DivError::DivideByZero.into());
}
let dividend = read_u256_from_stack(process, 9, "dividend")?;
let (quotient, remainder) = u256_divmod(dividend, divisor);
let q_felts = u256_to_u32_felts(quotient);
let r_felts = u256_to_u32_felts(remainder);
let mut advice = [Felt::from_u32(0); 16];
advice[0..4].copy_from_slice(&r_felts[4..8]);
advice[4..8].copy_from_slice(&r_felts[0..4]);
advice[8..12].copy_from_slice(&q_felts[4..8]);
advice[12..16].copy_from_slice(&q_felts[0..4]);
Ok(vec![AdviceMutation::extend_stack(advice)])
}
fn read_u256_from_stack(
process: &ProcessorState,
start: usize,
name: &'static str,
) -> Result<(u128, u128), EventError> {
let mut lo: u128 = 0;
let mut hi: u128 = 0;
for i in (0..8).rev() {
let limb = process.get_stack_item(start + i).as_canonical_u64();
if limb > u32::MAX as u64 {
return Err(U256DivError::NotU32Value {
value: limb,
position: name,
limb_index: i,
}
.into());
}
if i < 4 {
lo = (lo << 32) | limb as u128;
} else {
hi = (hi << 32) | limb as u128;
}
}
Ok((lo, hi))
}
fn u256_to_u32_felts(value: (u128, u128)) -> [Felt; 8] {
let (lo, hi) = value;
[
Felt::from_u32(lo as u32),
Felt::from_u32((lo >> 32) as u32),
Felt::from_u32((lo >> 64) as u32),
Felt::from_u32((lo >> 96) as u32),
Felt::from_u32(hi as u32),
Felt::from_u32((hi >> 32) as u32),
Felt::from_u32((hi >> 64) as u32),
Felt::from_u32((hi >> 96) as u32),
]
}
fn u256_divmod(a: (u128, u128), b: (u128, u128)) -> ((u128, u128), (u128, u128)) {
let (mut q_lo, mut q_hi) = (0u128, 0u128);
let (mut r_lo, mut r_hi) = (0u128, 0u128);
for bit in (0..256).rev() {
r_hi = (r_hi << 1) | (r_lo >> 127);
r_lo <<= 1;
let next_bit = if bit < 128 {
(a.0 >> bit) & 1
} else {
(a.1 >> (bit - 128)) & 1
};
r_lo |= next_bit;
let take = r_hi > b.1 || (r_hi == b.1 && r_lo >= b.0);
if take {
let (new_lo, borrow) = r_lo.overflowing_sub(b.0);
r_lo = new_lo;
r_hi = r_hi.wrapping_sub(b.1).wrapping_sub(borrow as u128);
if bit < 128 {
q_lo |= 1u128 << bit;
} else {
q_hi |= 1u128 << (bit - 128);
}
}
}
((q_lo, q_hi), (r_lo, r_hi))
}
#[derive(Debug, thiserror::Error)]
pub enum U256DivError {
#[error("division by zero")]
DivideByZero,
#[error("value {value} at {position} limb {limb_index} is not a valid u32")]
NotU32Value {
value: u64,
position: &'static str,
limb_index: usize,
},
}