miden_stdlib/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_core::EventName;
9use miden_processor::{AdviceMutation, EventError, ProcessState};
10
11use crate::handlers::u64_to_u32_elements;
12
13/// Event name for the u64_div operation.
14pub const U64_DIV_EVENT_NAME: EventName = EventName::new("stdlib::math::u64::u64_div");
15
16/// U64_DIV system event handler.
17///
18/// Pushes the result of [u64] division (both the quotient and the remainder) onto the advice
19/// stack.
20///
21/// Inputs:
22///   Operand stack: [event_id, b1, b0, a1, a0, ...]
23///   Advice stack: [...]
24///
25/// Outputs:
26///   Advice stack: [q0, q1, r0, r1, ...]
27///
28/// Where (a0, a1) and (b0, b1) are the 32-bit limbs of the dividend and the divisor
29/// respectively (with a0 representing the 32 lest significant bits and a1 representing the
30/// 32 most significant bits). Similarly, (q0, q1) and (r0, r1) represent the quotient and
31/// the remainder respectively.
32///
33/// # Errors
34/// Returns an error if the divisor is ZERO.
35pub fn handle_u64_div(process: &ProcessState) -> Result<Vec<AdviceMutation>, EventError> {
36    let divisor = {
37        let divisor_hi = process.get_stack_item(1).as_int();
38        let divisor_lo = process.get_stack_item(2).as_int();
39
40        // Ensure the divisor is a pair of u32 values
41        if divisor_hi > u32::MAX.into() {
42            return Err(U64DivError::NotU32Value {
43                value: divisor_hi,
44                position: "divisor_hi",
45            }
46            .into());
47        }
48        if divisor_lo > u32::MAX.into() {
49            return Err(U64DivError::NotU32Value {
50                value: divisor_lo,
51                position: "divisor_lo",
52            }
53            .into());
54        }
55
56        let divisor = (divisor_hi << 32) + divisor_lo;
57
58        if divisor == 0 {
59            return Err(U64DivError::DivideByZero.into());
60        }
61
62        divisor
63    };
64
65    let dividend = {
66        let dividend_hi = process.get_stack_item(3).as_int();
67        let dividend_lo = process.get_stack_item(4).as_int();
68
69        // Ensure the dividend is a pair of u32 values
70        if dividend_hi > u32::MAX.into() {
71            return Err(U64DivError::NotU32Value {
72                value: dividend_hi,
73                position: "dividend_hi",
74            }
75            .into());
76        }
77        if dividend_lo > u32::MAX.into() {
78            return Err(U64DivError::NotU32Value {
79                value: dividend_lo,
80                position: "dividend_lo",
81            }
82            .into());
83        }
84
85        (dividend_hi << 32) + dividend_lo
86    };
87
88    let quotient = dividend / divisor;
89    let remainder = dividend - quotient * divisor;
90
91    let (q_hi, q_lo) = u64_to_u32_elements(quotient);
92    let (r_hi, r_lo) = u64_to_u32_elements(remainder);
93
94    // Create mutations to extend the advice stack with the result.
95    // The values are pushed in reverse order to match the processor's behavior:
96    // r_hi, r_lo, q_hi, q_lo
97    let mutation = AdviceMutation::extend_stack([r_hi, r_lo, q_hi, q_lo]);
98    Ok(vec![mutation])
99}
100
101// ERROR TYPES
102// ================================================================================================
103
104/// Error types that can occur during U64_DIV operations.
105#[derive(Debug, thiserror::Error)]
106pub enum U64DivError {
107    /// Division by zero error.
108    #[error("division by zero")]
109    DivideByZero,
110
111    /// Value is not a valid u32.
112    #[error("value {value} at {position} is not a valid u32")]
113    NotU32Value { value: u64, position: &'static str },
114}