Skip to main content

miden_core_lib/handlers/
u64_div.rs

1//! U64_DIV system event handler for the Miden VM.
2//!
3//! This handler implements the U64_DIV operation that pushes the result of [u64] division
4//! (both the quotient and the remainder) onto the advice stack.
5
6use alloc::{vec, vec::Vec};
7
8use miden_processor::{
9    ProcessorState,
10    advice::AdviceMutation,
11    event::{EventError, EventName},
12};
13
14use crate::handlers::u64_to_u32_elements;
15
16/// Event name for the u64_div operation.
17pub const U64_DIV_EVENT_NAME: EventName = EventName::new("miden::core::math::u64::u64_div");
18
19/// U64_DIV system event handler.
20///
21/// Pushes the result of [u64] division (both the quotient and the remainder) onto the advice
22/// stack.
23///
24/// Inputs:
25///   Operand stack: [event_id, b_lo, b_hi, a_lo, a_hi, ...]
26///   Advice stack: [...]
27///
28/// Outputs:
29///   Advice stack: [q_lo, q_hi, r_lo, r_hi, ...]
30///
31/// Where (a_lo, a_hi) and (b_lo, b_hi) are the 32-bit limbs of the dividend and the divisor
32/// respectively (with lo representing the 32 least significant bits and hi representing the
33/// 32 most significant bits). Similarly, (q_lo, q_hi) and (r_lo, r_hi) represent the quotient
34/// and the remainder respectively.
35///
36/// # Errors
37/// Returns an error if the divisor is ZERO.
38pub fn handle_u64_div(process: &ProcessorState) -> Result<Vec<AdviceMutation>, EventError> {
39    // Read divisor from positions 1 (lo) and 2 (hi) - b is on top of stack
40    let divisor = {
41        let divisor_lo = process.get_stack_item(1).as_canonical_u64();
42        let divisor_hi = process.get_stack_item(2).as_canonical_u64();
43
44        // Ensure the divisor is a pair of u32 values
45        if divisor_lo > u32::MAX.into() {
46            return Err(U64DivError::NotU32Value {
47                value: divisor_lo,
48                position: "divisor_lo",
49            }
50            .into());
51        }
52        if divisor_hi > u32::MAX.into() {
53            return Err(U64DivError::NotU32Value {
54                value: divisor_hi,
55                position: "divisor_hi",
56            }
57            .into());
58        }
59
60        let divisor = (divisor_hi << 32) + divisor_lo;
61
62        if divisor == 0 {
63            return Err(U64DivError::DivideByZero.into());
64        }
65
66        divisor
67    };
68
69    // Read dividend from positions 3 (lo) and 4 (hi) - a is below b
70    let dividend = {
71        let dividend_lo = process.get_stack_item(3).as_canonical_u64();
72        let dividend_hi = process.get_stack_item(4).as_canonical_u64();
73
74        // Ensure the dividend is a pair of u32 values
75        if dividend_lo > u32::MAX.into() {
76            return Err(U64DivError::NotU32Value {
77                value: dividend_lo,
78                position: "dividend_lo",
79            }
80            .into());
81        }
82        if dividend_hi > u32::MAX.into() {
83            return Err(U64DivError::NotU32Value {
84                value: dividend_hi,
85                position: "dividend_hi",
86            }
87            .into());
88        }
89
90        (dividend_hi << 32) + dividend_lo
91    };
92
93    let quotient = dividend / divisor;
94    let remainder = dividend - quotient * divisor;
95
96    let (q_hi, q_lo) = u64_to_u32_elements(quotient);
97    let (r_hi, r_lo) = u64_to_u32_elements(remainder);
98
99    // Create mutations to extend the advice stack with the result.
100    // extend_stack([a,b,c,d]) puts 'a' on top due to reverse iteration + push_front
101    // So [q_hi, q_lo, r_hi, r_lo] puts q_hi on top
102    // After adv_push.2: pops q_hi then q_lo → operand stack [q_lo, q_hi, ...] (LE)
103    // After adv_push.2: pops r_hi then r_lo → operand stack [r_lo, r_hi, ...] (LE)
104    let mutation = AdviceMutation::extend_stack([q_hi, q_lo, r_hi, r_lo]);
105    Ok(vec![mutation])
106}
107
108// ERROR TYPES
109// ================================================================================================
110
111/// Error types that can occur during U64_DIV operations.
112#[derive(Debug, thiserror::Error)]
113pub enum U64DivError {
114    /// Division by zero error.
115    #[error("division by zero")]
116    DivideByZero,
117
118    /// Value is not a valid u32.
119    #[error("value {value} at {position} is not a valid u32")]
120    NotU32Value { value: u64, position: &'static str },
121}