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}