Skip to main content

miden_core/advice/
stack.rs

1use alloc::{collections::VecDeque, vec::Vec};
2
3use super::{AdviceInputs, AdviceMap};
4use crate::{Felt, Word, crypto::merkle::MerkleStore};
5
6// ADVICE STACK BUILDER
7// ================================================================================================
8
9/// A builder for constructing advice stack inputs with intuitive ordering.
10///
11/// The builder maintains a conceptual advice stack where index 0 is the "top" - i.e., the element
12/// that will be consumed first. Method names indicate which MASM instruction pattern they target,
13/// abstracting away the internal transformations needed for correct element ordering.
14///
15/// # Building Direction
16///
17/// Building happens "top-first": the first method call adds elements that will be consumed first
18/// by the MASM code. Each subsequent method call adds elements "below" the previous ones.
19///
20/// # Example
21///
22/// ```ignore
23/// let advice = AdviceStackBuilder::new()
24///     .push_for_adv_push(&[a, b, c])  // Consumed first by adv_push adv_push adv_push
25///     .push_word(word)                // Consumed second by adv_loadw (or adv_pushw)
26///     .build();
27/// ```
28#[derive(Clone, Debug, Default)]
29pub struct AdviceStackBuilder {
30    /// Conceptual stack where front (index 0) is "top" (consumed first).
31    stack: VecDeque<Felt>,
32}
33
34impl AdviceStackBuilder {
35    /// Creates a new empty builder.
36    pub fn new() -> Self {
37        Self::default()
38    }
39
40    // STATE MUTATORS
41    // --------------------------------------------------------------------------------------------
42
43    /// Pushes a single element onto the advice stack.
44    ///
45    /// Elements are consumed in FIFO order: first pushed = first consumed by advice operations.
46    pub fn push_element(&mut self, value: Felt) -> &mut Self {
47        self.stack.push_back(value);
48        self
49    }
50
51    /// Extends the advice stack with raw elements (already ordered top-to-bottom).
52    ///
53    /// Elements are consumed in FIFO order: first element in iter = first consumed.
54    pub fn push_elements<I>(&mut self, values: I) -> &mut Self
55    where
56        I: IntoIterator<Item = Felt>,
57    {
58        self.stack.extend(values);
59        self
60    }
61
62    /// Adds elements for consumption by multiple sequential `adv_push` instructions.
63    ///
64    /// After `repeat.n adv_push end`, the operand stack will have `slice[0]` on top.
65    ///
66    /// # How it works
67    ///
68    /// Each `adv_push` pops one element from the advice stack and pushes it to the operand
69    /// stack. Since each push goes to the top, the first-popped element ends up at the bottom
70    /// of the n elements, and the last-popped element ends up on top.
71    ///
72    /// Therefore, this method reverses the slice internally so that `slice[0]` is popped last
73    /// and ends up on top of the operand stack.
74    ///
75    /// # Example
76    ///
77    /// ```ignore
78    /// builder.push_for_adv_push(&[a, b, c]);
79    /// // MASM: adv_push adv_push adv_push
80    /// // Result: operand stack = [a, b, c, ...] with a on top
81    /// ```
82    pub fn push_for_adv_push(&mut self, slice: &[Felt]) -> &mut Self {
83        // Reverse the slice: we want slice[0] to be popped last (ending up on top of operand stack)
84        // So we add elements in reverse order to the back of our stack
85        for elem in slice.iter().rev() {
86            self.stack.push_back(*elem);
87        }
88        self
89    }
90
91    /// Adds a word for consumption by `adv_loadw` or `adv_pushw`.
92    ///
93    /// Both instructions consume the same 4 elements from the advice stack and place them on
94    /// the operand stack with `word[0]` on top; they differ only in whether the top operand
95    /// word is overwritten (`adv_loadw`) or the stack grows by 4 (`adv_pushw`).
96    ///
97    /// # Example
98    ///
99    /// ```ignore
100    /// builder.push_word([w0, w1, w2, w3].into());
101    /// // MASM: adv_loadw (or adv_pushw)
102    /// // Result: operand stack = [w0, w1, w2, w3, ...] with w0 on top
103    /// ```
104    pub fn push_word(&mut self, word: Word) -> &mut Self {
105        for elem in word.iter() {
106            self.stack.push_back(*elem);
107        }
108        self
109    }
110
111    /// Adds elements for sequential consumption by `adv_pipe` operations.
112    ///
113    /// Elements are consumed in order: `slice[0..8]` first, then `slice[8..16]`, etc.
114    /// No reversal is applied.
115    ///
116    /// # Panics
117    ///
118    /// Panics if the slice length is not a multiple of 8 (double-word aligned).
119    ///
120    /// # Example
121    ///
122    /// ```ignore
123    /// builder.push_for_adv_pipe(&elements); // elements.len() must be multiple of 8
124    /// // MASM: multiple adv_pipe calls
125    /// // Result: elements consumed in order [elements[0], elements[1], ...]
126    /// ```
127    pub fn push_for_adv_pipe(&mut self, slice: &[Felt]) -> &mut Self {
128        assert!(
129            slice.len().is_multiple_of(8),
130            "push_for_adv_pipe requires slice length to be a multiple of 8, got {}",
131            slice.len()
132        );
133
134        for elem in slice.iter() {
135            self.stack.push_back(*elem);
136        }
137        self
138    }
139
140    /// Extends the advice stack with u64 values converted to Felt.
141    ///
142    /// This is a convenience method for test data that is typically specified as u64.
143    /// Elements are consumed in FIFO order: first element = first consumed.
144    ///
145    /// # Example
146    ///
147    /// ```ignore
148    /// builder.push_u64_slice(&[1, 2, 3, 4, 5, 6, 7, 8]);
149    /// // Elements consumed in order: 1, 2, 3, 4, 5, 6, 7, 8
150    /// ```
151    pub fn push_u64_slice(&mut self, values: &[u64]) -> &mut Self {
152        self.stack.extend(values.iter().map(|&v| Felt::new_unchecked(v)));
153        self
154    }
155
156    // INPUT BUILDERS
157    // --------------------------------------------------------------------------------------------
158
159    /// Builds the `AdviceInputs` from the accumulated stack.
160    ///
161    /// The builder's conceptual stack (with index 0 as top) is converted to the format
162    /// expected by `AdviceInputs`, which will be reversed when creating an `AdviceProvider`.
163    pub fn build(self) -> AdviceInputs {
164        AdviceInputs {
165            stack: self.stack.into(),
166            map: AdviceMap::default(),
167            store: MerkleStore::default(),
168        }
169    }
170
171    /// Builds the `AdviceInputs` with additional map and store data.
172    pub fn build_with(self, map: AdviceMap, store: MerkleStore) -> AdviceInputs {
173        AdviceInputs { stack: self.stack.into(), map, store }
174    }
175
176    /// Builds just the advice stack as `Vec<u64>` for use with `build_test!` macro.
177    ///
178    /// This is a convenience method that avoids needing to modify the test infrastructure.
179    pub fn build_vec_u64(self) -> Vec<u64> {
180        self.stack.into_iter().map(|f| f.as_canonical_u64()).collect()
181    }
182
183    /// Consumes the builder and returns the accumulated elements as `Vec<Felt>`.
184    pub fn into_elements(self) -> Vec<Felt> {
185        self.stack.into_iter().collect()
186    }
187}