1use std::collections::BTreeMap;
2use std::sync::Arc;
3
4use super::FormulaPlaneMode;
5use super::arena::AstNodeId;
6use super::formula_source::{SourceFamilyId, SourceFormulaOrder};
7
8#[derive(Clone, Debug)]
9pub struct FormulaIngestRecord {
10 pub row: u32,
11 pub col: u32,
12 pub ast_id: AstNodeId,
13 pub formula_text: Option<Arc<str>>,
14 pub(crate) source_order: Option<SourceFormulaOrder>,
15 pub(crate) source_family: Option<SourceFamilyId>,
16 pub(crate) partition_owner: Option<SourceFamilyId>,
17 pub(crate) member_anchor: Option<(u32, u32)>,
21}
22
23impl FormulaIngestRecord {
24 pub fn new(row: u32, col: u32, ast_id: AstNodeId, formula_text: Option<Arc<str>>) -> Self {
25 Self {
26 row,
27 col,
28 ast_id,
29 formula_text,
30 source_order: None,
31 source_family: None,
32 partition_owner: None,
33 member_anchor: None,
34 }
35 }
36
37 pub(crate) fn member(row: u32, col: u32, template: AstNodeId, anchor: (u32, u32)) -> Self {
39 Self {
40 member_anchor: Some(anchor),
41 ..Self::new(row, col, template, None)
42 }
43 }
44
45 pub fn is_family_member(&self) -> bool {
48 self.member_anchor.is_some()
49 }
50
51 pub(crate) fn with_source_proof(
52 mut self,
53 source_order: SourceFormulaOrder,
54 source_family: Option<SourceFamilyId>,
55 partition_owner: Option<SourceFamilyId>,
56 ) -> Self {
57 self.source_order = Some(source_order);
58 self.source_family = source_family;
59 self.partition_owner = partition_owner;
60 self
61 }
62}
63
64#[derive(Debug, Default)]
70pub struct FormulaFamilyGrouper {
71 pub(crate) by_col: rustc_hash::FxHashMap<u32, (u32, GroupedFamily)>,
73 pub(crate) last: Option<(u32, u32, GroupedFamily)>,
75 pub(crate) members: u64,
77 pub(crate) counters: super::SourceFamilyCounters,
80 pub(crate) shared_roots: rustc_hash::FxHashMap<u64, GroupedFamily>,
83}
84
85impl FormulaFamilyGrouper {
86 pub fn new() -> Self {
87 Self::default()
88 }
89
90 pub fn members(&self) -> u64 {
92 self.members
93 }
94
95 #[doc(hidden)]
97 pub fn note_formula(&mut self) {
98 self.counters.formulas += 1;
99 }
100
101 #[doc(hidden)]
103 pub fn note_parse(&mut self, bytes: usize) {
104 self.counters.parse_calls += 1;
105 self.counters.parse_bytes += bytes as u64;
106 }
107
108 #[doc(hidden)]
110 pub fn note_parse_cache_hit(&mut self) {
111 self.counters.parse_cache_hits += 1;
112 }
113
114 pub(crate) fn attach_lexical(
117 &mut self,
118 row0: u32,
119 col0: u32,
120 lexical: Arc<super::source_family::LexicalTemplate>,
121 ) {
122 if let Some((row, family)) = self.by_col.get_mut(&col0)
123 && *row == row0
124 {
125 family.lexical = Some(Arc::clone(&lexical));
126 }
127 if let Some((row, col, family)) = self.last.as_mut()
128 && (*row, *col) == (row0, col0)
129 {
130 family.lexical = Some(lexical);
131 }
132 }
133
134 pub(crate) fn note(&mut self, row0: u32, col0: u32, family: GroupedFamily) {
135 self.by_col.insert(col0, (row0, family.clone()));
136 self.last = Some((row0, col0, family));
137 }
138}
139
140#[derive(Clone, Debug)]
145pub(crate) struct GroupedFamily {
146 pub(crate) template: AstNodeId,
147 pub(crate) anchor: (u32, u32),
148 pub(crate) rendered: Option<Option<Arc<[bool]>>>,
149 pub(crate) lexical: Option<Arc<super::source_family::LexicalTemplate>>,
152}
153
154#[derive(Clone, Debug)]
155pub struct FormulaIngestBatch {
156 pub sheet_name: String,
157 pub formulas: Vec<FormulaIngestRecord>,
158}
159
160impl FormulaIngestBatch {
161 pub fn new(sheet_name: impl Into<String>, formulas: Vec<FormulaIngestRecord>) -> Self {
162 Self {
163 sheet_name: sheet_name.into(),
164 formulas,
165 }
166 }
167
168 pub fn len(&self) -> usize {
169 self.formulas.len()
170 }
171
172 pub fn is_empty(&self) -> bool {
173 self.formulas.is_empty()
174 }
175}
176
177#[derive(Debug, Clone, PartialEq, Eq)]
178pub struct FormulaIngestReport {
179 pub mode: FormulaPlaneMode,
180 pub formula_cells_seen: u64,
181 pub graph_formula_cells_materialized: u64,
182
183 pub shadow_candidate_cells: u64,
184 pub shadow_accepted_span_cells: u64,
185 pub shadow_fallback_cells: u64,
186 pub shadow_templates_interned: u64,
187 pub shadow_spans_created: u64,
188
189 pub graph_formula_vertices_avoided_shadow: u64,
190 pub ast_roots_avoided_shadow: u64,
191 pub edge_rows_avoided_shadow: u64,
192
193 pub graph_vertices_created: u64,
194 pub graph_edges_created: u64,
195
196 pub source_formula_events: u64,
197 pub source_formula_records_spooled: u64,
198 pub source_spool_encoded_bytes: u64,
199 pub source_spool_peak_memory_bytes: u64,
200 pub source_spool_spilled_bytes: u64,
201 pub source_spool_spill_files: u64,
202 pub source_spool_replays: u64,
203 pub source_ordinary_events: u64,
204 pub source_shared_anchor_events: u64,
205 pub source_shared_descendant_events: u64,
206 pub source_unknown_events: u64,
207 pub source_families_seen: u64,
208 pub source_family_cells_seen: u64,
209 pub source_family_shadow_eligible: u64,
210 pub source_family_shadow_eligible_cells: u64,
211 pub source_family_promoted: u64,
212 pub source_family_promoted_cells: u64,
213 pub source_family_fallback: u64,
214 pub source_family_fallback_cells: u64,
215 pub source_forward_descendants: u64,
216 pub source_evidence_limit_fallbacks: u64,
217 pub source_evidence_peak_bytes: u64,
218 pub source_anchor_parses: u64,
219 pub source_anchor_asts: u64,
220 pub source_anchor_analyses: u64,
221 pub source_descendant_strings_avoided: u64,
222 pub source_descendant_events_avoided: u64,
223 pub source_descendant_analyses_avoided: u64,
224 pub source_compressed_families_prepared: u64,
225 pub source_compressed_cells_prepared: u64,
226 pub source_partitioned_families_seen: u64,
227 pub source_partitioned_families_prepared: u64,
228 pub source_partitioned_families_rejected: u64,
229 pub source_partition_fragments_prepared: u64,
230 pub source_partition_span_cells_prepared: u64,
231 pub source_partition_fallback_cells: u64,
232 pub source_partition_analyses_reused: u64,
233 pub source_partition_function_semantics: u64,
234 pub source_partition_holes: u64,
235 pub source_partition_ordinary_exceptions: u64,
236 pub source_partition_failures: u64,
237 pub source_partition_surviving_cells: u64,
238
239 pub fallback_reasons: BTreeMap<String, u64>,
240}
241
242impl Default for FormulaIngestReport {
243 fn default() -> Self {
244 Self {
245 mode: FormulaPlaneMode::Off,
246 formula_cells_seen: 0,
247 graph_formula_cells_materialized: 0,
248 shadow_candidate_cells: 0,
249 shadow_accepted_span_cells: 0,
250 shadow_fallback_cells: 0,
251 shadow_templates_interned: 0,
252 shadow_spans_created: 0,
253 graph_formula_vertices_avoided_shadow: 0,
254 ast_roots_avoided_shadow: 0,
255 edge_rows_avoided_shadow: 0,
256 graph_vertices_created: 0,
257 graph_edges_created: 0,
258 source_formula_events: 0,
259 source_formula_records_spooled: 0,
260 source_spool_encoded_bytes: 0,
261 source_spool_peak_memory_bytes: 0,
262 source_spool_spilled_bytes: 0,
263 source_spool_spill_files: 0,
264 source_spool_replays: 0,
265 source_ordinary_events: 0,
266 source_shared_anchor_events: 0,
267 source_shared_descendant_events: 0,
268 source_unknown_events: 0,
269 source_families_seen: 0,
270 source_family_cells_seen: 0,
271 source_family_shadow_eligible: 0,
272 source_family_shadow_eligible_cells: 0,
273 source_family_promoted: 0,
274 source_family_promoted_cells: 0,
275 source_family_fallback: 0,
276 source_family_fallback_cells: 0,
277 source_forward_descendants: 0,
278 source_evidence_limit_fallbacks: 0,
279 source_evidence_peak_bytes: 0,
280 source_anchor_parses: 0,
281 source_anchor_asts: 0,
282 source_anchor_analyses: 0,
283 source_descendant_strings_avoided: 0,
284 source_descendant_events_avoided: 0,
285 source_descendant_analyses_avoided: 0,
286 source_compressed_families_prepared: 0,
287 source_compressed_cells_prepared: 0,
288 source_partitioned_families_seen: 0,
289 source_partitioned_families_prepared: 0,
290 source_partitioned_families_rejected: 0,
291 source_partition_fragments_prepared: 0,
292 source_partition_span_cells_prepared: 0,
293 source_partition_fallback_cells: 0,
294 source_partition_analyses_reused: 0,
295 source_partition_function_semantics: 0,
296 source_partition_holes: 0,
297 source_partition_ordinary_exceptions: 0,
298 source_partition_failures: 0,
299 source_partition_surviving_cells: 0,
300 fallback_reasons: BTreeMap::new(),
301 }
302 }
303}
304
305impl FormulaIngestReport {
306 pub(crate) fn with_mode(mode: FormulaPlaneMode) -> Self {
307 Self {
308 mode,
309 ..Self::default()
310 }
311 }
312
313 pub(crate) fn accumulate(&mut self, other: &Self) {
314 self.formula_cells_seen = self
315 .formula_cells_seen
316 .saturating_add(other.formula_cells_seen);
317 self.graph_formula_cells_materialized = self
318 .graph_formula_cells_materialized
319 .saturating_add(other.graph_formula_cells_materialized);
320 self.shadow_candidate_cells = self
321 .shadow_candidate_cells
322 .saturating_add(other.shadow_candidate_cells);
323 self.shadow_accepted_span_cells = self
324 .shadow_accepted_span_cells
325 .saturating_add(other.shadow_accepted_span_cells);
326 self.shadow_fallback_cells = self
327 .shadow_fallback_cells
328 .saturating_add(other.shadow_fallback_cells);
329 self.shadow_templates_interned = self
330 .shadow_templates_interned
331 .saturating_add(other.shadow_templates_interned);
332 self.shadow_spans_created = self
333 .shadow_spans_created
334 .saturating_add(other.shadow_spans_created);
335 self.graph_formula_vertices_avoided_shadow = self
336 .graph_formula_vertices_avoided_shadow
337 .saturating_add(other.graph_formula_vertices_avoided_shadow);
338 self.ast_roots_avoided_shadow = self
339 .ast_roots_avoided_shadow
340 .saturating_add(other.ast_roots_avoided_shadow);
341 self.edge_rows_avoided_shadow = self
342 .edge_rows_avoided_shadow
343 .saturating_add(other.edge_rows_avoided_shadow);
344 self.graph_vertices_created = self
345 .graph_vertices_created
346 .saturating_add(other.graph_vertices_created);
347 self.graph_edges_created = self
348 .graph_edges_created
349 .saturating_add(other.graph_edges_created);
350 self.source_formula_events = self
351 .source_formula_events
352 .saturating_add(other.source_formula_events);
353 self.source_formula_records_spooled = self
354 .source_formula_records_spooled
355 .saturating_add(other.source_formula_records_spooled);
356 self.source_spool_encoded_bytes = self
357 .source_spool_encoded_bytes
358 .saturating_add(other.source_spool_encoded_bytes);
359 self.source_spool_peak_memory_bytes = self
360 .source_spool_peak_memory_bytes
361 .max(other.source_spool_peak_memory_bytes);
362 self.source_spool_spilled_bytes = self
363 .source_spool_spilled_bytes
364 .saturating_add(other.source_spool_spilled_bytes);
365 self.source_spool_spill_files = self
366 .source_spool_spill_files
367 .saturating_add(other.source_spool_spill_files);
368 self.source_spool_replays = self
369 .source_spool_replays
370 .saturating_add(other.source_spool_replays);
371 self.source_ordinary_events = self
372 .source_ordinary_events
373 .saturating_add(other.source_ordinary_events);
374 self.source_shared_anchor_events = self
375 .source_shared_anchor_events
376 .saturating_add(other.source_shared_anchor_events);
377 self.source_shared_descendant_events = self
378 .source_shared_descendant_events
379 .saturating_add(other.source_shared_descendant_events);
380 self.source_unknown_events = self
381 .source_unknown_events
382 .saturating_add(other.source_unknown_events);
383 self.source_families_seen = self
384 .source_families_seen
385 .saturating_add(other.source_families_seen);
386 self.source_family_cells_seen = self
387 .source_family_cells_seen
388 .saturating_add(other.source_family_cells_seen);
389 self.source_family_shadow_eligible = self
390 .source_family_shadow_eligible
391 .saturating_add(other.source_family_shadow_eligible);
392 self.source_family_shadow_eligible_cells = self
393 .source_family_shadow_eligible_cells
394 .saturating_add(other.source_family_shadow_eligible_cells);
395 self.source_family_promoted = self
396 .source_family_promoted
397 .saturating_add(other.source_family_promoted);
398 self.source_family_promoted_cells = self
399 .source_family_promoted_cells
400 .saturating_add(other.source_family_promoted_cells);
401 self.source_family_fallback = self
402 .source_family_fallback
403 .saturating_add(other.source_family_fallback);
404 self.source_family_fallback_cells = self
405 .source_family_fallback_cells
406 .saturating_add(other.source_family_fallback_cells);
407 self.source_forward_descendants = self
408 .source_forward_descendants
409 .saturating_add(other.source_forward_descendants);
410 self.source_evidence_limit_fallbacks = self
411 .source_evidence_limit_fallbacks
412 .saturating_add(other.source_evidence_limit_fallbacks);
413 self.source_evidence_peak_bytes = self
414 .source_evidence_peak_bytes
415 .max(other.source_evidence_peak_bytes);
416 self.source_anchor_parses = self
417 .source_anchor_parses
418 .saturating_add(other.source_anchor_parses);
419 self.source_anchor_asts = self
420 .source_anchor_asts
421 .saturating_add(other.source_anchor_asts);
422 self.source_anchor_analyses = self
423 .source_anchor_analyses
424 .saturating_add(other.source_anchor_analyses);
425 self.source_descendant_strings_avoided = self
426 .source_descendant_strings_avoided
427 .saturating_add(other.source_descendant_strings_avoided);
428 self.source_descendant_events_avoided = self
429 .source_descendant_events_avoided
430 .saturating_add(other.source_descendant_events_avoided);
431 self.source_descendant_analyses_avoided = self
432 .source_descendant_analyses_avoided
433 .saturating_add(other.source_descendant_analyses_avoided);
434 self.source_compressed_families_prepared = self
435 .source_compressed_families_prepared
436 .saturating_add(other.source_compressed_families_prepared);
437 self.source_compressed_cells_prepared = self
438 .source_compressed_cells_prepared
439 .saturating_add(other.source_compressed_cells_prepared);
440 self.source_partitioned_families_seen = self
441 .source_partitioned_families_seen
442 .saturating_add(other.source_partitioned_families_seen);
443 self.source_partitioned_families_prepared = self
444 .source_partitioned_families_prepared
445 .saturating_add(other.source_partitioned_families_prepared);
446 self.source_partitioned_families_rejected = self
447 .source_partitioned_families_rejected
448 .saturating_add(other.source_partitioned_families_rejected);
449 self.source_partition_fragments_prepared = self
450 .source_partition_fragments_prepared
451 .saturating_add(other.source_partition_fragments_prepared);
452 self.source_partition_span_cells_prepared = self
453 .source_partition_span_cells_prepared
454 .saturating_add(other.source_partition_span_cells_prepared);
455 self.source_partition_fallback_cells = self
456 .source_partition_fallback_cells
457 .saturating_add(other.source_partition_fallback_cells);
458 self.source_partition_analyses_reused = self
459 .source_partition_analyses_reused
460 .saturating_add(other.source_partition_analyses_reused);
461 self.source_partition_function_semantics = self
462 .source_partition_function_semantics
463 .saturating_add(other.source_partition_function_semantics);
464 self.source_partition_holes = self
465 .source_partition_holes
466 .saturating_add(other.source_partition_holes);
467 self.source_partition_ordinary_exceptions = self
468 .source_partition_ordinary_exceptions
469 .saturating_add(other.source_partition_ordinary_exceptions);
470 self.source_partition_failures = self
471 .source_partition_failures
472 .saturating_add(other.source_partition_failures);
473 self.source_partition_surviving_cells = self
474 .source_partition_surviving_cells
475 .saturating_add(other.source_partition_surviving_cells);
476 for (reason, count) in &other.fallback_reasons {
477 let total = self.fallback_reasons.entry(reason.clone()).or_default();
478 *total = total.saturating_add(*count);
479 }
480 }
481}