1use alloc::{format, string::ToString};
9
10use miden_core::field::{PrimeCharacteristicRing, QuadFelt};
11use miden_crypto::stark::air::RowWindow;
12
13use super::{
14 super::super::{
15 BoundaryBuilder, Challenges, Deg, LookupBatch, LookupBuilder, LookupColumn, LookupGroup,
16 LookupMessage,
17 },
18 DebugTraceState, MutualExclusionViolation, PushRecord,
19};
20use crate::Felt;
21
22pub struct DebugTraceBuilder<'a> {
27 main: RowWindow<'a, Felt>,
28 periodic_values: &'a [Felt],
29 challenges: &'a Challenges<QuadFelt>,
30 state: &'a mut DebugTraceState,
31 row_idx: usize,
32 column_idx: usize,
33}
34
35impl<'a> DebugTraceBuilder<'a> {
36 pub fn new(
37 main: RowWindow<'a, Felt>,
38 periodic_values: &'a [Felt],
39 challenges: &'a Challenges<QuadFelt>,
40 state: &'a mut DebugTraceState,
41 row_idx: usize,
42 ) -> Self {
43 Self {
44 main,
45 periodic_values,
46 challenges,
47 state,
48 row_idx,
49 column_idx: 0,
50 }
51 }
52}
53
54impl<'a> LookupBuilder for DebugTraceBuilder<'a> {
55 type F = Felt;
56 type Expr = Felt;
57 type Var = Felt;
58
59 type EF = QuadFelt;
60 type ExprEF = QuadFelt;
61 type VarEF = QuadFelt;
62
63 type PeriodicVar = Felt;
64
65 type MainWindow = RowWindow<'a, Felt>;
66
67 type Column<'c>
68 = DebugTraceColumn<'c>
69 where
70 Self: 'c;
71
72 fn main(&self) -> Self::MainWindow {
73 self.main
74 }
75
76 fn periodic_values(&self) -> &[Self::PeriodicVar] {
77 self.periodic_values
78 }
79
80 fn next_column<'c, R>(
81 &'c mut self,
82 f: impl FnOnce(&mut Self::Column<'c>) -> R,
83 _deg: Deg,
84 ) -> R {
85 let mut col = DebugTraceColumn {
86 challenges: self.challenges,
87 state: &mut *self.state,
88 row_idx: self.row_idx,
89 column_idx: self.column_idx,
90 next_group_idx: 0,
91 };
92 let result = f(&mut col);
93 self.column_idx += 1;
94 result
95 }
96}
97
98pub struct DebugTraceColumn<'c> {
102 challenges: &'c Challenges<QuadFelt>,
103 state: &'c mut DebugTraceState,
104 row_idx: usize,
105 column_idx: usize,
106 next_group_idx: usize,
107}
108
109impl<'c> DebugTraceColumn<'c> {
110 fn open_group<'g>(
115 &'g mut self,
116 is_cached_encoding: bool,
117 f: impl FnOnce(&mut DebugTraceGroup<'g>),
118 ) {
119 let group_idx = self.next_group_idx;
120 let column_idx = self.column_idx;
121 let row_idx = self.row_idx;
122
123 let mut group = DebugTraceGroup {
124 challenges: self.challenges,
125 state: &mut *self.state,
126 u: QuadFelt::ONE,
127 v: QuadFelt::ZERO,
128 row_idx,
129 column_idx,
130 group_idx,
131 check_mutex: is_cached_encoding,
132 active_flag_count: 0,
133 };
134 f(&mut group);
135
136 if group.check_mutex && group.active_flag_count > 1 {
137 group.state.mutex_violations.push(MutualExclusionViolation {
138 row: row_idx,
139 column_idx,
140 group_idx,
141 active_flags: group.active_flag_count,
142 });
143 }
144 let (v_col, u_col) = group.state.column_folds[column_idx];
146 group.state.column_folds[column_idx] = (v_col * group.u + group.v * u_col, u_col * group.u);
147
148 self.next_group_idx += 1;
149 }
150}
151
152impl<'c> LookupColumn for DebugTraceColumn<'c> {
153 type Expr = Felt;
154 type ExprEF = QuadFelt;
155
156 type Group<'g>
157 = DebugTraceGroup<'g>
158 where
159 Self: 'g;
160
161 fn group<'g>(
162 &'g mut self,
163 _name: &'static str,
164 f: impl FnOnce(&mut Self::Group<'g>),
165 _deg: Deg,
166 ) {
167 self.open_group(false, f);
168 }
169
170 fn group_with_cached_encoding<'g>(
171 &'g mut self,
172 _name: &'static str,
173 canonical: impl FnOnce(&mut Self::Group<'g>),
174 _encoded: impl FnOnce(&mut Self::Group<'g>),
175 _deg: Deg,
176 ) {
177 self.open_group(true, canonical);
182 }
183}
184
185pub struct DebugTraceGroup<'g> {
189 challenges: &'g Challenges<QuadFelt>,
190 state: &'g mut DebugTraceState,
191 u: QuadFelt,
192 v: QuadFelt,
193 row_idx: usize,
194 column_idx: usize,
195 group_idx: usize,
196 check_mutex: bool,
198 active_flag_count: usize,
199}
200
201impl<'g> DebugTraceGroup<'g> {
202 fn track_mutex(&mut self, flag: Felt) {
205 if self.check_mutex && flag != Felt::ZERO {
206 self.active_flag_count += 1;
207 }
208 }
209
210 fn record(&mut self, msg_repr: alloc::string::String, denom: QuadFelt, multiplicity: Felt) {
213 *self.state.balances.entry(denom).or_insert(Felt::ZERO) += multiplicity;
214 self.state.push_log.push(PushRecord {
215 row: self.row_idx,
216 column_idx: self.column_idx,
217 group_idx: self.group_idx,
218 msg_repr,
219 denom,
220 multiplicity,
221 });
222 }
223}
224
225impl<'g> LookupGroup for DebugTraceGroup<'g> {
226 type Expr = Felt;
227 type ExprEF = QuadFelt;
228
229 type Batch<'b>
230 = DebugTraceBatch<'b>
231 where
232 Self: 'b;
233
234 fn insert<M>(
235 &mut self,
236 _name: &'static str,
237 flag: Felt,
238 multiplicity: Felt,
239 msg: impl FnOnce() -> M,
240 _deg: Deg,
241 ) where
242 M: LookupMessage<Felt, QuadFelt>,
243 {
244 self.track_mutex(flag);
245 if flag == Felt::ZERO {
246 return;
247 }
248 let built = msg();
249 let v_msg = built.encode(self.challenges);
250 self.record(format!("{built:?}"), v_msg, multiplicity);
251 self.u += (v_msg - QuadFelt::ONE) * flag;
252 self.v += flag * multiplicity;
253 }
254
255 fn batch<'b>(
256 &'b mut self,
257 _name: &'static str,
258 flag: Felt,
259 build: impl FnOnce(&mut Self::Batch<'b>),
260 _deg: Deg,
261 ) {
262 self.track_mutex(flag);
263 let active = flag != Felt::ZERO;
264 let (n, d) = {
265 let mut batch = DebugTraceBatch {
266 challenges: self.challenges,
267 state: &mut *self.state,
268 active,
269 n: QuadFelt::ZERO,
270 d: QuadFelt::ONE,
271 row_idx: self.row_idx,
272 column_idx: self.column_idx,
273 group_idx: self.group_idx,
274 };
275 build(&mut batch);
276 (batch.n, batch.d)
277 };
278 self.u += (d - QuadFelt::ONE) * flag;
279 self.v += n * flag;
280 }
281
282 fn beta_powers(&self) -> &[QuadFelt] {
283 &self.challenges.beta_powers[..]
284 }
285
286 fn bus_prefix(&self, bus_id: usize) -> QuadFelt {
287 self.challenges.bus_prefix[bus_id]
288 }
289
290 fn insert_encoded(
291 &mut self,
292 _name: &'static str,
293 flag: Felt,
294 multiplicity: Felt,
295 encoded: impl FnOnce() -> QuadFelt,
296 _deg: Deg,
297 ) {
298 self.track_mutex(flag);
299 if flag == Felt::ZERO {
300 return;
301 }
302 let v_msg = encoded();
303 self.record("<encoded>".to_string(), v_msg, multiplicity);
304 self.u += (v_msg - QuadFelt::ONE) * flag;
305 self.v += flag * multiplicity;
306 }
307}
308
309pub struct DebugTraceBatch<'b> {
313 challenges: &'b Challenges<QuadFelt>,
314 state: &'b mut DebugTraceState,
315 active: bool,
319 n: QuadFelt,
320 d: QuadFelt,
321 row_idx: usize,
323 column_idx: usize,
324 group_idx: usize,
325}
326
327impl<'b> DebugTraceBatch<'b> {
328 fn record(&mut self, msg_repr: alloc::string::String, denom: QuadFelt, multiplicity: Felt) {
329 *self.state.balances.entry(denom).or_insert(Felt::ZERO) += multiplicity;
330 self.state.push_log.push(PushRecord {
331 row: self.row_idx,
332 column_idx: self.column_idx,
333 group_idx: self.group_idx,
334 msg_repr,
335 denom,
336 multiplicity,
337 });
338 }
339}
340
341impl<'b> LookupBatch for DebugTraceBatch<'b> {
342 type Expr = Felt;
343 type ExprEF = QuadFelt;
344
345 fn insert<M>(&mut self, _name: &'static str, multiplicity: Felt, msg: M, _deg: Deg)
346 where
347 M: LookupMessage<Felt, QuadFelt>,
348 {
349 let v_msg = msg.encode(self.challenges);
350 if self.active {
351 self.record(format!("{msg:?}"), v_msg, multiplicity);
352 }
353 let d_prev = self.d;
354 self.n = self.n * v_msg + d_prev * multiplicity;
355 self.d *= v_msg;
356 }
357
358 fn insert_encoded(
359 &mut self,
360 _name: &'static str,
361 multiplicity: Felt,
362 encoded: impl FnOnce() -> QuadFelt,
363 _deg: Deg,
364 ) {
365 let v_msg = encoded();
366 if self.active {
367 self.record("<encoded>".to_string(), v_msg, multiplicity);
368 }
369 let d_prev = self.d;
370 self.n = self.n * v_msg + d_prev * multiplicity;
371 self.d *= v_msg;
372 }
373}
374
375pub struct DebugBoundaryEmitter<'a> {
383 pub(super) challenges: &'a Challenges<QuadFelt>,
384 pub(super) state: &'a mut DebugTraceState,
385 pub(super) public_values: &'a [Felt],
386 pub(super) var_len_public_inputs: &'a [&'a [Felt]],
387}
388
389impl<'a> BoundaryBuilder for DebugBoundaryEmitter<'a> {
390 type F = Felt;
391 type EF = QuadFelt;
392
393 fn public_values(&self) -> &[Felt] {
394 self.public_values
395 }
396
397 fn var_len_public_inputs(&self) -> &[&[Felt]] {
398 self.var_len_public_inputs
399 }
400
401 fn insert<M>(&mut self, name: &'static str, multiplicity: Felt, msg: M)
402 where
403 M: LookupMessage<Felt, QuadFelt>,
404 {
405 let denom = msg.encode(self.challenges);
406 *self.state.balances.entry(denom).or_insert(Felt::ZERO) += multiplicity;
407 self.state.push_log.push(PushRecord {
408 row: usize::MAX,
409 column_idx: usize::MAX,
410 group_idx: usize::MAX,
411 msg_repr: format!("[boundary:{name}] {msg:?}"),
412 denom,
413 multiplicity,
414 });
415 }
416}