miden_core/mast/node/
loop_node.rs1use alloc::{boxed::Box, vec::Vec};
2use core::fmt;
3
4#[cfg(feature = "serde")]
5use serde::{Deserialize, Serialize};
6
7use super::{MastForestContributor, MastNodeExt, fingerprint_with_child_fingerprints};
8use crate::{
9 Felt, Word,
10 chiplets::hasher,
11 mast::{MastForest, MastForestError, MastNodeId},
12 operations::opcodes,
13 prettier::PrettyPrint,
14 utils::{Idx, LookupByIdx},
15};
16
17#[derive(Debug, Clone, PartialEq, Eq)]
28#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
29#[cfg_attr(all(feature = "arbitrary", test), miden_test_serde_macros::serde_test)]
30pub struct LoopNode {
31 body: MastNodeId,
32 digest: Word,
33}
34
35impl LoopNode {
37 pub const DOMAIN: Felt = Felt::new_unchecked(opcodes::LOOP as u64);
39}
40
41impl LoopNode {
42 pub fn body(&self) -> MastNodeId {
44 self.body
45 }
46}
47
48impl LoopNode {
52 pub(super) fn to_display<'a>(&'a self, mast_forest: &'a MastForest) -> impl fmt::Display + 'a {
53 LoopNodePrettyPrint { loop_node: self, mast_forest }
54 }
55
56 pub(super) fn to_pretty_print<'a>(
57 &'a self,
58 mast_forest: &'a MastForest,
59 ) -> impl PrettyPrint + 'a {
60 LoopNodePrettyPrint { loop_node: self, mast_forest }
61 }
62}
63
64struct LoopNodePrettyPrint<'a> {
65 loop_node: &'a LoopNode,
66 mast_forest: &'a MastForest,
67}
68
69impl PrettyPrint for LoopNodePrettyPrint<'_> {
70 fn render(&self) -> crate::prettier::Document {
71 use crate::prettier::*;
72
73 let loop_body = self.mast_forest[self.loop_node.body].to_pretty_print(self.mast_forest);
74
75 indent(4, const_text("loop") + nl() + loop_body.render()) + nl() + const_text("end")
76 }
77}
78
79impl fmt::Display for LoopNodePrettyPrint<'_> {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 use crate::prettier::PrettyPrint;
82 self.pretty_print(f)
83 }
84}
85
86impl MastNodeExt for LoopNode {
90 fn digest(&self) -> Word {
101 self.digest
102 }
103
104 fn to_display<'a>(&'a self, mast_forest: &'a MastForest) -> Box<dyn fmt::Display + 'a> {
105 Box::new(LoopNode::to_display(self, mast_forest))
106 }
107
108 fn to_pretty_print<'a>(&'a self, mast_forest: &'a MastForest) -> Box<dyn PrettyPrint + 'a> {
109 Box::new(LoopNode::to_pretty_print(self, mast_forest))
110 }
111
112 fn has_children(&self) -> bool {
113 true
114 }
115
116 fn append_children_to(&self, target: &mut Vec<MastNodeId>) {
117 target.push(self.body());
118 }
119
120 fn for_each_child<F>(&self, mut f: F)
121 where
122 F: FnMut(MastNodeId),
123 {
124 f(self.body());
125 }
126
127 fn domain(&self) -> Felt {
128 Self::DOMAIN
129 }
130
131 type Builder = LoopNodeBuilder;
132
133 fn to_builder(self, _forest: &MastForest) -> Self::Builder {
134 LoopNodeBuilder::new(self.body).with_digest(self.digest)
135 }
136}
137
138#[cfg(all(feature = "arbitrary", test))]
142impl proptest::prelude::Arbitrary for LoopNode {
143 type Parameters = ();
144
145 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
146 use proptest::prelude::*;
147
148 use crate::Felt;
149
150 (any::<MastNodeId>(), any::<[u64; 4]>())
152 .prop_map(|(body, digest_array)| {
153 let digest = Word::from(digest_array.map(Felt::new_unchecked));
155 LoopNode {
157 body,
158 digest,
159 }
160 })
161 .no_shrink() .boxed()
163 }
164
165 type Strategy = proptest::prelude::BoxedStrategy<Self>;
166}
167
168#[derive(Debug)]
171pub struct LoopNodeBuilder {
172 body: MastNodeId,
173 digest: Option<Word>,
174}
175
176impl LoopNodeBuilder {
177 pub fn new(body: MastNodeId) -> Self {
179 Self { body, digest: None }
180 }
181
182 pub fn build(self, mast_forest: &MastForest) -> Result<LoopNode, MastForestError> {
184 if self.body.to_usize() >= mast_forest.nodes.len() {
185 return Err(MastForestError::NodeIdOverflow(self.body, mast_forest.nodes.len()));
186 }
187
188 let digest = if let Some(forced_digest) = self.digest {
190 forced_digest
191 } else {
192 let body_hash = mast_forest[self.body].digest();
193
194 hasher::merge_in_domain(&[body_hash, Word::default()], LoopNode::DOMAIN)
195 };
196
197 Ok(LoopNode { body: self.body, digest })
198 }
199
200 pub(in crate::mast) fn build_linked(self) -> Result<LoopNode, MastForestError> {
201 Ok(LoopNode {
202 body: self.body,
203 digest: self.digest.ok_or(MastForestError::DigestRequiredForDeserialization)?,
204 })
205 }
206}
207
208impl MastForestContributor for LoopNodeBuilder {
209 fn add_to_forest(self, forest: &mut MastForest) -> Result<MastNodeId, MastForestError> {
210 let node = self.build(forest)?;
211
212 let node_id = forest.nodes.push(node.into()).map_err(|_| MastForestError::TooManyNodes)?;
215
216 Ok(node_id)
217 }
218
219 fn fingerprint_for_node(
220 &self,
221 forest: &MastForest,
222 hash_by_node_id: &impl LookupByIdx<MastNodeId, Word>,
223 ) -> Result<Word, MastForestError> {
224 let node_digest = if let Some(forced_digest) = self.digest {
225 forced_digest
226 } else {
227 let body_hash = forest[self.body].digest();
228
229 hasher::merge_in_domain(&[body_hash, Word::default()], LoopNode::DOMAIN)
230 };
231
232 fingerprint_with_child_fingerprints(node_digest, &[self.body], forest, hash_by_node_id)
233 }
234
235 fn remap_children(self, remapping: &impl LookupByIdx<MastNodeId, MastNodeId>) -> Self {
236 LoopNodeBuilder {
237 body: *remapping.get(self.body).unwrap_or(&self.body),
238 digest: self.digest,
239 }
240 }
241
242 fn with_digest(mut self, digest: Word) -> Self {
243 self.digest = Some(digest);
244 self
245 }
246}
247
248impl LoopNodeBuilder {
249 pub(in crate::mast) fn add_to_forest_relaxed(
259 self,
260 forest: &mut MastForest,
261 ) -> Result<MastNodeId, MastForestError> {
262 let Some(digest) = self.digest else {
265 return Err(MastForestError::DigestRequiredForDeserialization);
266 };
267
268 let node_id = forest
271 .nodes
272 .push(LoopNode { body: self.body, digest }.into())
273 .map_err(|_| MastForestError::TooManyNodes)?;
274
275 Ok(node_id)
276 }
277}
278
279#[cfg(any(test, feature = "arbitrary"))]
280impl proptest::prelude::Arbitrary for LoopNodeBuilder {
281 type Parameters = LoopNodeBuilderParams;
282 type Strategy = proptest::strategy::BoxedStrategy<Self>;
283
284 fn arbitrary_with(params: Self::Parameters) -> Self::Strategy {
285 use proptest::prelude::*;
286
287 let _ = params;
288 any::<MastNodeId>().prop_map(Self::new).boxed()
289 }
290}
291
292#[cfg(any(test, feature = "arbitrary"))]
294#[derive(Clone, Debug, Default)]
295pub struct LoopNodeBuilderParams {}