miden_core/mast/node/
split_node.rs1use alloc::{boxed::Box, vec::Vec};
2use core::fmt;
3
4use super::{
5 MastForestContributor, MastNodeContext, MastNodeExt, fingerprint_with_child_fingerprints,
6};
7use crate::{
8 Felt, Word,
9 chiplets::hasher,
10 mast::{MastForest, MastForestError, MastNodeId},
11 operations::opcodes,
12 prettier::PrettyPrint,
13 utils::LookupByIdx,
14};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct SplitNode {
27 branches: [MastNodeId; 2],
28 digest: Word,
29}
30
31impl SplitNode {
33 pub const DOMAIN: Felt = Felt::new_unchecked(opcodes::SPLIT as u64);
35}
36
37impl SplitNode {
39 pub fn on_true(&self) -> MastNodeId {
41 self.branches[0]
42 }
43
44 pub fn on_false(&self) -> MastNodeId {
46 self.branches[1]
47 }
48}
49
50impl SplitNode {
54 pub(super) fn to_display<'a>(&'a self, mast_forest: &'a MastForest) -> impl fmt::Display + 'a {
55 SplitNodePrettyPrint { split_node: self, mast_forest }
56 }
57
58 pub(super) fn to_pretty_print<'a>(
59 &'a self,
60 mast_forest: &'a MastForest,
61 ) -> impl PrettyPrint + 'a {
62 SplitNodePrettyPrint { split_node: self, mast_forest }
63 }
64}
65
66struct SplitNodePrettyPrint<'a> {
67 split_node: &'a SplitNode,
68 mast_forest: &'a MastForest,
69}
70
71impl PrettyPrint for SplitNodePrettyPrint<'_> {
72 #[rustfmt::skip]
73 fn render(&self) -> crate::prettier::Document {
74 use crate::prettier::*;
75
76 let true_branch = self.mast_forest[self.split_node.on_true()].to_pretty_print(self.mast_forest);
77 let false_branch = self.mast_forest[self.split_node.on_false()].to_pretty_print(self.mast_forest);
78
79 let mut doc = Document::Empty;
80 doc += indent(4, const_text("if.true") + nl() + true_branch.render()) + nl();
81 doc += indent(4, const_text("else") + nl() + false_branch.render());
82 doc += nl() + const_text("end");
83 doc
84 }
85}
86
87impl fmt::Display for SplitNodePrettyPrint<'_> {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 use crate::prettier::PrettyPrint;
90 self.pretty_print(f)
91 }
92}
93
94impl MastNodeExt for SplitNode {
98 fn digest(&self) -> Word {
110 self.digest
111 }
112
113 fn to_display<'a>(&'a self, mast_forest: &'a MastForest) -> Box<dyn fmt::Display + 'a> {
114 Box::new(SplitNode::to_display(self, mast_forest))
115 }
116
117 fn to_pretty_print<'a>(&'a self, mast_forest: &'a MastForest) -> Box<dyn PrettyPrint + 'a> {
118 Box::new(SplitNode::to_pretty_print(self, mast_forest))
119 }
120
121 fn has_children(&self) -> bool {
122 true
123 }
124
125 fn append_children_to(&self, target: &mut Vec<MastNodeId>) {
126 target.push(self.on_true());
127 target.push(self.on_false());
128 }
129
130 fn for_each_child<F>(&self, mut f: F)
131 where
132 F: FnMut(MastNodeId),
133 {
134 f(self.on_true());
135 f(self.on_false());
136 }
137
138 fn domain(&self) -> Felt {
139 Self::DOMAIN
140 }
141
142 type Builder = SplitNodeBuilder;
143
144 fn to_builder(self, _forest: &MastForest) -> Self::Builder {
145 SplitNodeBuilder::new(self.branches).with_digest(self.digest)
146 }
147}
148
149#[derive(Debug)]
152pub struct SplitNodeBuilder {
153 branches: [MastNodeId; 2],
154 digest: Option<Word>,
155}
156
157impl SplitNodeBuilder {
158 pub fn new(branches: [MastNodeId; 2]) -> Self {
160 Self { branches, digest: None }
161 }
162
163 pub fn build(self, context: &impl MastNodeContext) -> Result<SplitNode, MastForestError> {
165 let true_branch = context.get_node_by_id(self.branches[0]).ok_or_else(|| {
166 MastForestError::NodeIdOverflow(self.branches[0], context.node_count())
167 })?;
168 let false_branch = context.get_node_by_id(self.branches[1]).ok_or_else(|| {
169 MastForestError::NodeIdOverflow(self.branches[1], context.node_count())
170 })?;
171
172 let digest = if let Some(forced_digest) = self.digest {
174 forced_digest
175 } else {
176 let true_branch_hash = true_branch.digest();
177 let false_branch_hash = false_branch.digest();
178
179 hasher::merge_in_domain(&[true_branch_hash, false_branch_hash], SplitNode::DOMAIN)
180 };
181
182 Ok(SplitNode { branches: self.branches, digest })
183 }
184
185 pub(in crate::mast) fn build_linked(self) -> Result<SplitNode, MastForestError> {
186 Ok(SplitNode {
187 branches: self.branches,
188 digest: self.digest.ok_or(MastForestError::DigestRequiredForDeserialization)?,
189 })
190 }
191}
192
193#[cfg(any(test, feature = "arbitrary"))]
194impl SplitNodeBuilder {
195 pub fn add_to_forest(self, forest: &mut MastForest) -> Result<MastNodeId, MastForestError> {
197 let node = self.build(forest)?;
198 forest.nodes.push(node.into()).map_err(|_| MastForestError::TooManyNodes)
199 }
200}
201
202impl MastForestContributor for SplitNodeBuilder {
203 fn fingerprint_for_node(
204 &self,
205 context: &impl MastNodeContext,
206 hash_by_node_id: &impl LookupByIdx<MastNodeId, Word>,
207 ) -> Result<Word, MastForestError> {
208 let node_digest = if let Some(forced_digest) = self.digest {
209 forced_digest
210 } else {
211 let if_branch_hash = context
212 .get_node_by_id(self.branches[0])
213 .ok_or_else(|| {
214 MastForestError::NodeIdOverflow(self.branches[0], context.node_count())
215 })?
216 .digest();
217 let else_branch_hash = context
218 .get_node_by_id(self.branches[1])
219 .ok_or_else(|| {
220 MastForestError::NodeIdOverflow(self.branches[1], context.node_count())
221 })?
222 .digest();
223
224 hasher::merge_in_domain(&[if_branch_hash, else_branch_hash], SplitNode::DOMAIN)
225 };
226
227 fingerprint_with_child_fingerprints(node_digest, &self.branches, context, hash_by_node_id)
228 }
229
230 fn remap_children(self, remapping: &impl LookupByIdx<MastNodeId, MastNodeId>) -> Self {
231 SplitNodeBuilder {
232 branches: [
233 *remapping.get(self.branches[0]).unwrap_or(&self.branches[0]),
234 *remapping.get(self.branches[1]).unwrap_or(&self.branches[1]),
235 ],
236 digest: self.digest,
237 }
238 }
239
240 fn with_digest(mut self, digest: Word) -> Self {
241 self.digest = Some(digest);
242 self
243 }
244}
245
246#[cfg(any(test, feature = "arbitrary"))]
247impl proptest::prelude::Arbitrary for SplitNodeBuilder {
248 type Parameters = ();
249 type Strategy = proptest::strategy::BoxedStrategy<Self>;
250
251 fn arbitrary_with(_params: Self::Parameters) -> Self::Strategy {
252 use proptest::prelude::*;
253
254 any::<[MastNodeId; 2]>().prop_map(Self::new).boxed()
255 }
256}