1use crate::SheetId;
2use crate::engine::named_range::{NameScope, NamedDefinition, NamedRange};
3use crate::engine::sheet_registry::SheetRegistry;
4use formualizer_common::{ExcelError, ExcelErrorKind, LiteralValue};
5use formualizer_parse::parser::{ASTNode, ReferenceType};
6use rustc_hash::{FxHashMap, FxHashSet};
7
8#[cfg(test)]
9#[derive(Debug, Default, Clone)]
10pub struct GraphInstrumentation {
11 pub edges_added: u64,
12 pub stripe_inserts: u64,
13 pub stripe_removes: u64,
14}
15
16mod ast_utils;
17pub mod editor;
18mod formula_analysis;
19mod names;
20mod range_deps;
21mod sheets;
22pub mod snapshot;
23mod sources;
24mod tables;
25
26use super::arena::{AstNodeId, DataStore, ValueRef};
27use super::delta_edges::CsrMutableEdges;
28use super::sheet_index::SheetIndex;
29use super::vertex::{VertexId, VertexKind};
30use super::vertex_store::{FIRST_NORMAL_VERTEX, VertexStore};
31use crate::engine::topo::{
32 GraphAdapter,
33 pk::{DynamicTopo, PkConfig},
34};
35use crate::reference::{CellRef, Coord, SharedRangeRef, SharedRef, SharedSheetLocator};
36use formualizer_common::Coord as AbsCoord;
37pub use editor::change_log::{ChangeEvent, ChangeLog};
40
41#[derive(Debug, Clone, PartialEq, Eq, Hash)]
45pub enum DependencyRef {
46 Cell(VertexId),
48 Range {
50 sheet: String,
51 start_row: u32,
52 start_col: u32,
53 end_row: u32, end_col: u32, },
56 WholeColumn { sheet: String, col: u32 },
58 WholeRow { sheet: String, row: u32 },
60}
61
62#[derive(Debug, Clone, Hash, PartialEq, Eq)]
64pub struct StripeKey {
65 pub sheet_id: SheetId,
66 pub stripe_type: StripeType,
67 pub index: u32, }
69
70#[derive(Debug, Clone, Hash, PartialEq, Eq)]
71pub enum StripeType {
72 Row,
73 Column,
74 Block, }
76
77const BLOCK_H: u32 = 256;
79const BLOCK_W: u32 = 256;
80
81pub fn block_index(row: u32, col: u32) -> u32 {
82 (row / BLOCK_H) << 16 | (col / BLOCK_W)
83}
84
85#[derive(Debug, Clone)]
88pub struct OperationSummary {
89 pub affected_vertices: Vec<VertexId>,
91 pub created_placeholders: Vec<CellRef>,
93}
94
95#[derive(Debug)]
97pub struct DependencyGraph {
98 store: VertexStore,
100
101 edges: CsrMutableEdges,
103
104 data_store: DataStore,
106 vertex_values: FxHashMap<VertexId, ValueRef>,
107 vertex_formulas: FxHashMap<VertexId, AstNodeId>,
108
109 cell_to_vertex: FxHashMap<CellRef, VertexId>,
111
112 dirty_vertices: FxHashSet<VertexId>,
114 volatile_vertices: FxHashSet<VertexId>,
115
116 formula_to_range_deps: FxHashMap<VertexId, Vec<SharedRangeRef<'static>>>,
119
120 stripe_to_dependents: FxHashMap<StripeKey, FxHashSet<VertexId>>,
123
124 sheet_indexes: FxHashMap<SheetId, SheetIndex>,
127
128 sheet_reg: SheetRegistry,
130 default_sheet_id: SheetId,
131
132 named_ranges: FxHashMap<String, NamedRange>,
135
136 sheet_named_ranges: FxHashMap<(SheetId, String), NamedRange>,
138
139 vertex_to_names: FxHashMap<VertexId, Vec<VertexId>>,
141
142 name_vertex_lookup: FxHashMap<VertexId, (NameScope, String)>,
144
145 pending_name_links: FxHashMap<String, Vec<(SheetId, VertexId)>>,
147
148 tables: FxHashMap<String, tables::TableEntry>,
150 table_vertex_lookup: FxHashMap<VertexId, String>,
151
152 source_scalars: FxHashMap<String, sources::SourceScalarEntry>,
154 source_tables: FxHashMap<String, sources::SourceTableEntry>,
155 source_vertex_lookup: FxHashMap<VertexId, String>,
156
157 name_vertex_seq: u32,
159
160 source_vertex_seq: u32,
162
163 cell_to_name_dependents: FxHashMap<VertexId, FxHashSet<VertexId>>,
165 name_to_cell_dependencies: FxHashMap<VertexId, Vec<VertexId>>,
167
168 config: super::EvalConfig,
170
171 pk_order: Option<DynamicTopo<VertexId>>,
173
174 spill_anchor_to_cells: FxHashMap<VertexId, Vec<CellRef>>,
176 spill_cell_to_anchor: FxHashMap<CellRef, VertexId>,
177
178 first_load_assume_new: bool,
180 ensure_touched_sheets: FxHashSet<SheetId>,
181
182 #[cfg(test)]
183 instr: GraphInstrumentation,
184}
185
186impl Default for DependencyGraph {
187 fn default() -> Self {
188 Self::new()
189 }
190}
191
192impl DependencyGraph {
193 pub fn range_expansion_limit(&self) -> usize {
195 self.config.range_expansion_limit
196 }
197
198 pub fn get_config(&self) -> &super::EvalConfig {
199 &self.config
200 }
201
202 pub fn plan_dependencies<'a, I>(
204 &mut self,
205 items: I,
206 policy: &formualizer_parse::parser::CollectPolicy,
207 volatile: Option<&[bool]>,
208 ) -> Result<crate::engine::plan::DependencyPlan, formualizer_common::ExcelError>
209 where
210 I: IntoIterator<Item = (&'a str, u32, u32, &'a formualizer_parse::parser::ASTNode)>,
211 {
212 crate::engine::plan::build_dependency_plan(
213 &mut self.sheet_reg,
214 items.into_iter(),
215 policy,
216 volatile,
217 )
218 }
219
220 pub fn ensure_vertices_batch(
223 &mut self,
224 coords: &[(SheetId, AbsCoord)],
225 ) -> Vec<(AbsCoord, u32)> {
226 use rustc_hash::FxHashMap;
227 let mut grouped: FxHashMap<SheetId, Vec<AbsCoord>> = FxHashMap::default();
228 for (sid, pc) in coords.iter().copied() {
229 let addr = CellRef::new(sid, Coord::new(pc.row(), pc.col(), true, true));
230 if self.cell_to_vertex.contains_key(&addr) {
231 continue;
232 }
233 grouped.entry(sid).or_default().push(pc);
234 }
235 let mut add_batch: Vec<(AbsCoord, u32)> = Vec::new();
236 for (sid, pcs) in grouped {
237 if pcs.is_empty() {
238 continue;
239 }
240 self.ensure_touched_sheets.insert(sid);
242 let vids = self.store.allocate_contiguous(sid, &pcs, 0x00);
243 for (i, pc) in pcs.iter().enumerate() {
244 let vid = vids[i];
245 add_batch.push((*pc, vid.0));
246 let addr = CellRef::new(sid, Coord::new(pc.row(), pc.col(), true, true));
247 self.cell_to_vertex.insert(addr, vid);
248 match self.config.sheet_index_mode {
250 crate::engine::SheetIndexMode::Eager
251 | crate::engine::SheetIndexMode::FastBatch => {
252 self.sheet_index_mut(sid).add_vertex(*pc, vid);
253 }
254 crate::engine::SheetIndexMode::Lazy => {
255 }
257 }
258 }
259 }
260 if !add_batch.is_empty() {
261 self.edges.add_vertices_batch(&add_batch);
262 }
263 add_batch
264 }
265
266 pub fn set_first_load_assume_new(&mut self, enabled: bool) {
268 self.first_load_assume_new = enabled;
269 }
270
271 pub fn reset_ensure_touched(&mut self) {
273 self.ensure_touched_sheets.clear();
274 }
275
276 pub fn store_asts_batch<'a, I>(&mut self, asts: I) -> Vec<AstNodeId>
278 where
279 I: IntoIterator<Item = &'a formualizer_parse::parser::ASTNode>,
280 {
281 self.data_store.store_asts_batch(asts, &self.sheet_reg)
282 }
283
284 pub fn vid_for_sid_pc(&self, sid: SheetId, pc: AbsCoord) -> Option<VertexId> {
286 let addr = CellRef::new(sid, Coord::new(pc.row(), pc.col(), true, true));
287 self.cell_to_vertex.get(&addr).copied()
288 }
289
290 pub fn vid_for_plan_idx(
292 &self,
293 plan: &crate::engine::plan::DependencyPlan,
294 idx: u32,
295 ) -> Option<VertexId> {
296 let (sid, pc) = plan.global_cells.get(idx as usize).copied()?;
297 self.vid_for_sid_pc(sid, pc)
298 }
299
300 pub fn assign_formula_vertex(&mut self, vid: VertexId, ast_id: AstNodeId, volatile: bool) {
302 if self.vertex_formulas.contains_key(&vid) {
303 self.remove_dependent_edges(vid);
304 }
305 self.store
306 .set_kind(vid, crate::engine::vertex::VertexKind::FormulaScalar);
307 self.vertex_values.remove(&vid);
308 self.vertex_formulas.insert(vid, ast_id);
309 self.mark_volatile(vid, volatile);
310 self.mark_vertex_dirty(vid);
312 }
313
314 pub fn add_edges_nobatch(&mut self, dependent: VertexId, dependencies: &[VertexId]) {
316 self.add_dependent_edges_nobatch(dependent, dependencies);
317 }
318
319 pub fn iter_vertex_ids(&self) -> impl Iterator<Item = VertexId> + '_ {
321 self.store.all_vertices()
322 }
323
324 pub fn vertex_coord(&self, vid: VertexId) -> AbsCoord {
326 self.store.coord(vid)
327 }
328
329 pub fn vertex_count(&self) -> usize {
331 self.store.len()
332 }
333
334 pub fn build_edges_from_adjacency(
336 &mut self,
337 adjacency: Vec<(u32, Vec<u32>)>,
338 coords: Vec<AbsCoord>,
339 vertex_ids: Vec<u32>,
340 ) {
341 self.edges
342 .build_from_adjacency(adjacency, coords, vertex_ids);
343 }
344 pub fn used_row_bounds_for_columns(
346 &self,
347 sheet_id: SheetId,
348 start_col: u32,
349 end_col: u32,
350 ) -> Option<(u32, u32)> {
351 if let Some(index) = self.sheet_indexes.get(&sheet_id)
353 && !index.is_empty()
354 {
355 let mut min_r: Option<u32> = None;
356 let mut max_r: Option<u32> = None;
357 for vid in index.vertices_in_col_range(start_col, end_col) {
358 let r = self.store.coord(vid).row();
359 min_r = Some(min_r.map(|m| m.min(r)).unwrap_or(r));
360 max_r = Some(max_r.map(|m| m.max(r)).unwrap_or(r));
361 }
362 return match (min_r, max_r) {
363 (Some(a), Some(b)) => Some((a, b)),
364 _ => None,
365 };
366 }
367 let mut min_r: Option<u32> = None;
369 let mut max_r: Option<u32> = None;
370 for cref in self.cell_to_vertex.keys() {
371 if cref.sheet_id == sheet_id {
372 let c = cref.coord.col();
373 if c >= start_col && c <= end_col {
374 let r = cref.coord.row();
375 min_r = Some(min_r.map(|m| m.min(r)).unwrap_or(r));
376 max_r = Some(max_r.map(|m| m.max(r)).unwrap_or(r));
377 }
378 }
379 }
380 match (min_r, max_r) {
381 (Some(a), Some(b)) => Some((a, b)),
382 _ => None,
383 }
384 }
385
386 pub fn finalize_sheet_index(&mut self, sheet: &str) {
388 let Some(sheet_id) = self.sheet_reg.get_id(sheet) else {
389 return;
390 };
391 if let Some(idx) = self.sheet_indexes.get(&sheet_id)
393 && !idx.is_empty()
394 {
395 return;
396 }
397 let mut idx = SheetIndex::new();
398 let mut batch: Vec<(AbsCoord, VertexId)> = Vec::with_capacity(self.cell_to_vertex.len());
400 for (cref, vid) in &self.cell_to_vertex {
401 if cref.sheet_id == sheet_id {
402 batch.push((AbsCoord::new(cref.coord.row(), cref.coord.col()), *vid));
403 }
404 }
405 idx.add_vertices_batch(&batch);
407 self.sheet_indexes.insert(sheet_id, idx);
408 }
409
410 pub fn set_sheet_index_mode(&mut self, mode: crate::engine::SheetIndexMode) {
411 self.config.sheet_index_mode = mode;
412 }
413
414 pub fn used_col_bounds_for_rows(
416 &self,
417 sheet_id: SheetId,
418 start_row: u32,
419 end_row: u32,
420 ) -> Option<(u32, u32)> {
421 if let Some(index) = self.sheet_indexes.get(&sheet_id)
422 && !index.is_empty()
423 {
424 let mut min_c: Option<u32> = None;
425 let mut max_c: Option<u32> = None;
426 for vid in index.vertices_in_row_range(start_row, end_row) {
427 let c = self.store.coord(vid).col();
428 min_c = Some(min_c.map(|m| m.min(c)).unwrap_or(c));
429 max_c = Some(max_c.map(|m| m.max(c)).unwrap_or(c));
430 }
431 return match (min_c, max_c) {
432 (Some(a), Some(b)) => Some((a, b)),
433 _ => None,
434 };
435 }
436 let mut min_c: Option<u32> = None;
438 let mut max_c: Option<u32> = None;
439 for cref in self.cell_to_vertex.keys() {
440 if cref.sheet_id == sheet_id {
441 let r = cref.coord.row();
442 if r >= start_row && r <= end_row {
443 let c = cref.coord.col();
444 min_c = Some(min_c.map(|m| m.min(c)).unwrap_or(c));
445 max_c = Some(max_c.map(|m| m.max(c)).unwrap_or(c));
446 }
447 }
448 }
449 match (min_c, max_c) {
450 (Some(a), Some(b)) => Some((a, b)),
451 _ => None,
452 }
453 }
454
455 pub fn sheet_has_formulas(&self, sheet_id: SheetId) -> bool {
457 for &vid in self.vertex_formulas.keys() {
459 if self.store.sheet_id(vid) == sheet_id {
460 return true;
461 }
462 }
463 false
464 }
465 pub fn new() -> Self {
466 Self::new_with_config(super::EvalConfig::default())
467 }
468
469 pub fn new_with_config(config: super::EvalConfig) -> Self {
470 let mut sheet_reg = SheetRegistry::new();
471 let default_sheet_id = sheet_reg.id_for(&config.default_sheet_name);
472
473 let mut g = Self {
474 store: VertexStore::new(),
475 edges: CsrMutableEdges::new(),
476 data_store: DataStore::new(),
477 vertex_values: FxHashMap::default(),
478 vertex_formulas: FxHashMap::default(),
479 cell_to_vertex: FxHashMap::default(),
480 dirty_vertices: FxHashSet::default(),
481 volatile_vertices: FxHashSet::default(),
482 formula_to_range_deps: FxHashMap::default(),
483 stripe_to_dependents: FxHashMap::default(),
484 sheet_indexes: FxHashMap::default(),
485 sheet_reg,
486 default_sheet_id,
487 named_ranges: FxHashMap::default(),
488 sheet_named_ranges: FxHashMap::default(),
489 vertex_to_names: FxHashMap::default(),
490 name_vertex_lookup: FxHashMap::default(),
491 pending_name_links: FxHashMap::default(),
492 tables: FxHashMap::default(),
493 table_vertex_lookup: FxHashMap::default(),
494 source_scalars: FxHashMap::default(),
495 source_tables: FxHashMap::default(),
496 source_vertex_lookup: FxHashMap::default(),
497 name_vertex_seq: 0,
498 source_vertex_seq: 0,
499 cell_to_name_dependents: FxHashMap::default(),
500 name_to_cell_dependencies: FxHashMap::default(),
501 config: config.clone(),
502 pk_order: None,
503 spill_anchor_to_cells: FxHashMap::default(),
504 spill_cell_to_anchor: FxHashMap::default(),
505 first_load_assume_new: false,
506 ensure_touched_sheets: FxHashSet::default(),
507 #[cfg(test)]
508 instr: GraphInstrumentation::default(),
509 };
510
511 if config.use_dynamic_topo {
512 let nodes = g
514 .store
515 .all_vertices()
516 .filter(|&id| g.store.vertex_exists_active(id));
517 let mut pk = DynamicTopo::new(
518 nodes,
519 PkConfig {
520 visit_budget: config.pk_visit_budget,
521 compaction_interval_ops: config.pk_compaction_interval_ops,
522 },
523 );
524 let adapter = GraphAdapter { g: &g };
526 pk.rebuild_full(&adapter);
527 g.pk_order = Some(pk);
528 }
529
530 g
531 }
532
533 pub(crate) fn pk_layers_for(&self, subset: &[VertexId]) -> Option<Vec<crate::engine::Layer>> {
535 let pk = self.pk_order.as_ref()?;
536 let adapter = crate::engine::topo::GraphAdapter { g: self };
537 let layers = pk.layers_for(&adapter, subset, self.config.max_layer_width);
538 Some(
539 layers
540 .into_iter()
541 .map(|vs| crate::engine::Layer { vertices: vs })
542 .collect(),
543 )
544 }
545
546 #[inline]
547 pub(crate) fn dynamic_topo_enabled(&self) -> bool {
548 self.pk_order.is_some()
549 }
550
551 #[cfg(test)]
552 pub fn reset_instr(&mut self) {
553 self.instr = GraphInstrumentation::default();
554 }
555
556 #[cfg(test)]
557 pub fn instr(&self) -> GraphInstrumentation {
558 self.instr.clone()
559 }
560
561 pub fn begin_batch(&mut self) {
563 self.edges.begin_batch();
564 }
565
566 pub fn end_batch(&mut self) {
568 self.edges.end_batch();
569 }
570
571 pub fn default_sheet_id(&self) -> SheetId {
572 self.default_sheet_id
573 }
574
575 pub fn default_sheet_name(&self) -> &str {
576 self.sheet_reg.name(self.default_sheet_id)
577 }
578
579 pub fn set_default_sheet_by_name(&mut self, name: &str) {
580 self.default_sheet_id = self.sheet_id_mut(name);
581 }
582
583 pub fn set_default_sheet_by_id(&mut self, id: SheetId) {
584 self.default_sheet_id = id;
585 }
586
587 pub fn sheet_id_mut(&mut self, name: &str) -> SheetId {
589 self.sheet_reg.id_for(name)
590 }
591
592 pub fn sheet_id(&self, name: &str) -> Option<SheetId> {
593 self.sheet_reg.get_id(name)
594 }
595
596 fn resolve_existing_sheet_id(&self, name: &str) -> Result<SheetId, ExcelError> {
598 self.sheet_id(name).ok_or_else(|| {
599 ExcelError::new(ExcelErrorKind::Ref).with_message(format!("Sheet not found: {name}"))
600 })
601 }
602
603 pub fn sheet_name(&self, id: SheetId) -> &str {
605 self.sheet_reg.name(id)
606 }
607
608 pub fn sheet_reg(&self) -> &SheetRegistry {
610 &self.sheet_reg
611 }
612
613 pub(crate) fn data_store(&self) -> &DataStore {
614 &self.data_store
615 }
616
617 pub fn to_a1(&self, cell_ref: CellRef) -> String {
619 format!("{}!{}", self.sheet_name(cell_ref.sheet_id), cell_ref.coord)
620 }
621
622 pub(crate) fn vertex_len(&self) -> usize {
623 self.store.len()
624 }
625
626 pub fn sheet_index_mut(&mut self, sheet_id: SheetId) -> &mut SheetIndex {
629 self.sheet_indexes.entry(sheet_id).or_default()
630 }
631
632 pub fn sheet_index(&self, sheet_id: SheetId) -> Option<&SheetIndex> {
634 self.sheet_indexes.get(&sheet_id)
635 }
636
637 pub fn set_cell_value(
639 &mut self,
640 sheet: &str,
641 row: u32,
642 col: u32,
643 value: LiteralValue,
644 ) -> Result<OperationSummary, ExcelError> {
645 let sheet_id = self.sheet_id_mut(sheet);
646 let coord = Coord::from_excel(row, col, true, true);
648 let addr = CellRef::new(sheet_id, coord);
649 let mut created_placeholders = Vec::new();
650
651 let vertex_id = if let Some(&existing_id) = self.cell_to_vertex.get(&addr) {
652 let is_formula = matches!(
654 self.store.kind(existing_id),
655 VertexKind::FormulaScalar | VertexKind::FormulaArray
656 );
657
658 if is_formula {
659 self.remove_dependent_edges(existing_id);
660 self.vertex_formulas.remove(&existing_id);
661 }
662
663 self.store.set_kind(existing_id, VertexKind::Cell);
665 let value_ref = self.data_store.store_value(value);
666 self.vertex_values.insert(existing_id, value_ref);
667 existing_id
668 } else {
669 created_placeholders.push(addr);
671 let packed_coord = AbsCoord::from_excel(row, col);
672 let vertex_id = self.store.allocate(packed_coord, sheet_id, 0x01); self.edges.add_vertex(packed_coord, vertex_id.0);
676
677 self.sheet_index_mut(sheet_id)
679 .add_vertex(packed_coord, vertex_id);
680
681 self.store.set_kind(vertex_id, VertexKind::Cell);
682 let value_ref = self.data_store.store_value(value);
683 self.vertex_values.insert(vertex_id, value_ref);
684 self.cell_to_vertex.insert(addr, vertex_id);
685 vertex_id
686 };
687
688 Ok(OperationSummary {
689 affected_vertices: self.mark_dirty(vertex_id),
690 created_placeholders,
691 })
692 }
693
694 pub fn reserve_cells(&mut self, additional: usize) {
696 self.store.reserve(additional);
697 self.vertex_values.reserve(additional);
698 self.cell_to_vertex.reserve(additional);
699 }
701
702 pub fn set_cell_value_bulk_untracked(
704 &mut self,
705 sheet: &str,
706 row: u32,
707 col: u32,
708 value: LiteralValue,
709 ) {
710 let sheet_id = self.sheet_id_mut(sheet);
711 let coord = Coord::from_excel(row, col, true, true);
712 let addr = CellRef::new(sheet_id, coord);
713 if let Some(&existing_id) = self.cell_to_vertex.get(&addr) {
714 let value_ref = self.data_store.store_value(value);
716 self.vertex_values.insert(existing_id, value_ref);
717 self.store.set_kind(existing_id, VertexKind::Cell);
718 return;
719 }
720 let packed_coord = AbsCoord::from_excel(row, col);
721 let vertex_id = self.store.allocate(packed_coord, sheet_id, 0x00); self.edges.add_vertex(packed_coord, vertex_id.0);
723 self.sheet_index_mut(sheet_id)
724 .add_vertex(packed_coord, vertex_id);
725 self.store.set_kind(vertex_id, VertexKind::Cell);
726 let value_ref = self.data_store.store_value(value);
727 self.vertex_values.insert(vertex_id, value_ref);
728 self.cell_to_vertex.insert(addr, vertex_id);
729 }
730
731 pub fn bulk_insert_values<I>(&mut self, sheet: &str, cells: I)
733 where
734 I: IntoIterator<Item = (u32, u32, LiteralValue)>,
735 {
736 use web_time::Instant;
737 let t0 = Instant::now();
738 let collected: Vec<(u32, u32, LiteralValue)> = cells.into_iter().collect();
740 if collected.is_empty() {
741 return;
742 }
743 let sheet_id = self.sheet_id_mut(sheet);
744 self.reserve_cells(collected.len());
745 let t_reserve = Instant::now();
746 let mut new_vertices: Vec<(AbsCoord, u32)> = Vec::with_capacity(collected.len());
747 let mut index_items: Vec<(AbsCoord, VertexId)> = Vec::with_capacity(collected.len());
748 let mut new_value_coords: Vec<(AbsCoord, VertexId)> = Vec::with_capacity(collected.len());
750 let mut new_value_literals: Vec<LiteralValue> = Vec::with_capacity(collected.len());
751 let assume_new = self.first_load_assume_new
753 && self
754 .sheet_id(sheet)
755 .map(|sid| !self.ensure_touched_sheets.contains(&sid))
756 .unwrap_or(false);
757
758 for (row, col, value) in collected {
759 let coord = Coord::from_excel(row, col, true, true);
760 let addr = CellRef::new(sheet_id, coord);
761 if !assume_new && let Some(&existing_id) = self.cell_to_vertex.get(&addr) {
762 let value_ref = self.data_store.store_value(value);
763 self.vertex_values.insert(existing_id, value_ref);
764 self.store.set_kind(existing_id, VertexKind::Cell);
765 continue;
766 }
767 let packed = AbsCoord::from_excel(row, col);
768 let vertex_id = self.store.allocate(packed, sheet_id, 0x00);
769 self.store.set_kind(vertex_id, VertexKind::Cell);
770 new_value_coords.push((packed, vertex_id));
772 new_value_literals.push(value);
773 self.cell_to_vertex.insert(addr, vertex_id);
774 new_vertices.push((packed, vertex_id.0));
775 index_items.push((packed, vertex_id));
776 }
777 if !new_value_literals.is_empty() {
779 let vrefs = self.data_store.store_values_batch(new_value_literals);
780 debug_assert_eq!(vrefs.len(), new_value_coords.len());
781 for (i, (_pc, vid)) in new_value_coords.iter().enumerate() {
782 self.vertex_values.insert(*vid, vrefs[i]);
783 }
784 }
785 let t_after_alloc = Instant::now();
786 if !new_vertices.is_empty() {
787 let t_edges_start = Instant::now();
788 self.edges.add_vertices_batch(&new_vertices);
789 let t_edges_done = Instant::now();
790
791 match self.config.sheet_index_mode {
792 crate::engine::SheetIndexMode::Eager => {
793 self.sheet_index_mut(sheet_id)
794 .add_vertices_batch(&index_items);
795 }
796 crate::engine::SheetIndexMode::Lazy => {
797 }
799 crate::engine::SheetIndexMode::FastBatch => {
800 self.sheet_index_mut(sheet_id)
802 .add_vertices_batch(&index_items);
803 }
804 }
805 let t_index_done = Instant::now();
806 }
807 }
808
809 pub fn set_cell_formula(
811 &mut self,
812 sheet: &str,
813 row: u32,
814 col: u32,
815 ast: ASTNode,
816 ) -> Result<OperationSummary, ExcelError> {
817 let volatile = self.is_ast_volatile(&ast);
818 self.set_cell_formula_with_volatility(sheet, row, col, ast, volatile)
819 }
820
821 pub fn set_cell_formula_with_volatility(
823 &mut self,
824 sheet: &str,
825 row: u32,
826 col: u32,
827 ast: ASTNode,
828 volatile: bool,
829 ) -> Result<OperationSummary, ExcelError> {
830 let dbg = std::env::var("FZ_DEBUG_LOAD")
831 .ok()
832 .is_some_and(|v| v != "0");
833 let dep_ms_thresh: u128 = std::env::var("FZ_DEBUG_DEP_MS")
834 .ok()
835 .and_then(|s| s.parse().ok())
836 .unwrap_or(0);
837 let sample_n: usize = std::env::var("FZ_DEBUG_SAMPLE_N")
838 .ok()
839 .and_then(|s| s.parse().ok())
840 .unwrap_or(0);
841 let t0 = if dbg {
842 Some(web_time::Instant::now())
843 } else {
844 None
845 };
846 let sheet_id = self.sheet_id_mut(sheet);
847 let coord = Coord::from_excel(row, col, true, true);
848 let addr = CellRef::new(sheet_id, coord);
849
850 let t_dep0 = if dbg {
852 Some(web_time::Instant::now())
853 } else {
854 None
855 };
856 let (
857 new_dependencies,
858 new_range_dependencies,
859 mut created_placeholders,
860 named_dependencies,
861 ) = self.extract_dependencies(&ast, sheet_id)?;
862 if let (true, Some(t)) = (dbg, t_dep0) {
863 let elapsed = t.elapsed().as_millis();
864 let do_log = (dep_ms_thresh > 0 && elapsed >= dep_ms_thresh)
866 || (sample_n > 0 && (row as usize).is_multiple_of(sample_n));
867 if dep_ms_thresh == 0 && sample_n == 0 {
868 if row.is_multiple_of(1000) {
870 eprintln!(
871 "[fz][dep] {}!{} extracted: deps={}, ranges={}, placeholders={}, names={} in {} ms",
872 self.sheet_name(sheet_id),
873 crate::reference::Coord::from_excel(row, col, true, true),
874 new_dependencies.len(),
875 new_range_dependencies.len(),
876 created_placeholders.len(),
877 named_dependencies.len(),
878 elapsed
879 );
880 }
881 } else if do_log {
882 eprintln!(
883 "[fz][dep] {}!{} extracted: deps={}, ranges={}, placeholders={}, names={} in {} ms",
884 self.sheet_name(sheet_id),
885 crate::reference::Coord::from_excel(row, col, true, true),
886 new_dependencies.len(),
887 new_range_dependencies.len(),
888 created_placeholders.len(),
889 named_dependencies.len(),
890 elapsed
891 );
892 }
893 }
894
895 let addr_vertex_id = self.get_or_create_vertex(&addr, &mut created_placeholders);
897
898 if new_dependencies.contains(&addr_vertex_id) {
899 return Err(ExcelError::new(ExcelErrorKind::Circ)
900 .with_message("Self-reference detected".to_string()));
901 }
902
903 for &name_vertex in &named_dependencies {
904 let mut visited = FxHashSet::default();
905 if self.name_depends_on_vertex(name_vertex, addr_vertex_id, &mut visited) {
906 return Err(ExcelError::new(ExcelErrorKind::Circ)
907 .with_message("Circular reference through named range".to_string()));
908 }
909 }
910
911 self.remove_dependent_edges(addr_vertex_id);
913 self.detach_vertex_from_names(addr_vertex_id);
914
915 self.store
917 .set_kind(addr_vertex_id, VertexKind::FormulaScalar);
918 let ast_id = self.data_store.store_ast(&ast, &self.sheet_reg);
919 self.vertex_formulas.insert(addr_vertex_id, ast_id);
920 self.store.set_dirty(addr_vertex_id, true);
921
922 self.vertex_values.remove(&addr_vertex_id);
924
925 self.mark_volatile(addr_vertex_id, volatile);
926
927 if !named_dependencies.is_empty() {
928 self.attach_vertex_to_names(addr_vertex_id, &named_dependencies);
929 }
930
931 if let (true, Some(t)) = (dbg, t0) {
932 let elapsed = t.elapsed().as_millis();
933 let log_set = dep_ms_thresh > 0 && elapsed >= dep_ms_thresh;
934 if log_set {
935 eprintln!(
936 "[fz][set] {}!{} total {} ms",
937 self.sheet_name(sheet_id),
938 crate::reference::Coord::from_excel(row, col, true, true),
939 elapsed
940 );
941 }
942 }
943
944 self.add_dependent_edges(addr_vertex_id, &new_dependencies);
946 self.add_range_dependent_edges(addr_vertex_id, &new_range_dependencies, sheet_id);
947
948 Ok(OperationSummary {
949 affected_vertices: self.mark_dirty(addr_vertex_id),
950 created_placeholders,
951 })
952 }
953
954 pub fn set_cell_value_ref(
955 &mut self,
956 cell: formualizer_common::SheetCellRef<'_>,
957 value: LiteralValue,
958 ) -> Result<OperationSummary, ExcelError> {
959 let owned = cell.into_owned();
960 let sheet_id = match owned.sheet {
961 formualizer_common::SheetLocator::Id(id) => id,
962 formualizer_common::SheetLocator::Name(name) => self.sheet_id_mut(name.as_ref()),
963 formualizer_common::SheetLocator::Current => self.default_sheet_id,
964 };
965 let sheet_name = self.sheet_name(sheet_id).to_string();
966 self.set_cell_value(
967 &sheet_name,
968 owned.coord.row() + 1,
969 owned.coord.col() + 1,
970 value,
971 )
972 }
973
974 pub fn set_cell_formula_ref(
975 &mut self,
976 cell: formualizer_common::SheetCellRef<'_>,
977 ast: ASTNode,
978 ) -> Result<OperationSummary, ExcelError> {
979 let owned = cell.into_owned();
980 let sheet_id = match owned.sheet {
981 formualizer_common::SheetLocator::Id(id) => id,
982 formualizer_common::SheetLocator::Name(name) => self.sheet_id_mut(name.as_ref()),
983 formualizer_common::SheetLocator::Current => self.default_sheet_id,
984 };
985 let sheet_name = self.sheet_name(sheet_id).to_string();
986 self.set_cell_formula(
987 &sheet_name,
988 owned.coord.row() + 1,
989 owned.coord.col() + 1,
990 ast,
991 )
992 }
993
994 pub fn get_cell_value_ref(
995 &self,
996 cell: formualizer_common::SheetCellRef<'_>,
997 ) -> Option<LiteralValue> {
998 let owned = cell.into_owned();
999 let sheet_id = match owned.sheet {
1000 formualizer_common::SheetLocator::Id(id) => id,
1001 formualizer_common::SheetLocator::Name(name) => self.sheet_id(name.as_ref())?,
1002 formualizer_common::SheetLocator::Current => self.default_sheet_id,
1003 };
1004 let sheet_name = self.sheet_name(sheet_id);
1005 self.get_cell_value(sheet_name, owned.coord.row() + 1, owned.coord.col() + 1)
1006 }
1007
1008 pub fn get_cell_value(&self, sheet: &str, row: u32, col: u32) -> Option<LiteralValue> {
1010 let sheet_id = self.sheet_reg.get_id(sheet)?;
1011 let coord = Coord::from_excel(row, col, true, true);
1012 let addr = CellRef::new(sheet_id, coord);
1013
1014 self.cell_to_vertex.get(&addr).and_then(|&vertex_id| {
1015 self.vertex_values
1017 .get(&vertex_id)
1018 .map(|&value_ref| self.data_store.retrieve_value(value_ref))
1019 })
1020 }
1021
1022 fn mark_dirty(&mut self, vertex_id: VertexId) -> Vec<VertexId> {
1024 let mut affected = FxHashSet::default();
1025 let mut to_visit = Vec::new();
1026 let mut visited_for_propagation = FxHashSet::default();
1027
1028 let is_formula = matches!(
1031 self.store.kind(vertex_id),
1032 VertexKind::FormulaScalar
1033 | VertexKind::FormulaArray
1034 | VertexKind::NamedScalar
1035 | VertexKind::NamedArray
1036 );
1037
1038 if is_formula {
1039 to_visit.push(vertex_id);
1040 } else {
1041 affected.insert(vertex_id);
1043 }
1044
1045 {
1047 let dependents = self.get_dependents(vertex_id);
1049 to_visit.extend(&dependents);
1050
1051 if let Some(name_set) = self.cell_to_name_dependents.get(&vertex_id) {
1052 for &name_vertex in name_set {
1053 to_visit.push(name_vertex);
1054 }
1055 }
1056
1057 let view = self.store.view(vertex_id);
1059 let row = view.row();
1060 let col = view.col();
1061 let dirty_sheet_id = view.sheet_id();
1062
1063 let mut potential_dependents = FxHashSet::default();
1065
1066 let column_key = StripeKey {
1068 sheet_id: dirty_sheet_id,
1069 stripe_type: StripeType::Column,
1070 index: col,
1071 };
1072 if let Some(dependents) = self.stripe_to_dependents.get(&column_key) {
1073 potential_dependents.extend(dependents);
1074 }
1075
1076 let row_key = StripeKey {
1078 sheet_id: dirty_sheet_id,
1079 stripe_type: StripeType::Row,
1080 index: row,
1081 };
1082 if let Some(dependents) = self.stripe_to_dependents.get(&row_key) {
1083 potential_dependents.extend(dependents);
1084 }
1085
1086 if self.config.enable_block_stripes {
1088 let block_key = StripeKey {
1089 sheet_id: dirty_sheet_id,
1090 stripe_type: StripeType::Block,
1091 index: block_index(row, col),
1092 };
1093 if let Some(dependents) = self.stripe_to_dependents.get(&block_key) {
1094 potential_dependents.extend(dependents);
1095 }
1096 }
1097
1098 for &dep_id in &potential_dependents {
1100 if let Some(ranges) = self.formula_to_range_deps.get(&dep_id) {
1101 for range in ranges {
1102 let range_sheet_id = match range.sheet {
1103 SharedSheetLocator::Id(id) => id,
1104 _ => dirty_sheet_id,
1105 };
1106 if range_sheet_id != dirty_sheet_id {
1107 continue;
1108 }
1109 let sr0 = range.start_row.map(|b| b.index).unwrap_or(0);
1110 let er0 = range.end_row.map(|b| b.index).unwrap_or(u32::MAX);
1111 let sc0 = range.start_col.map(|b| b.index).unwrap_or(0);
1112 let ec0 = range.end_col.map(|b| b.index).unwrap_or(u32::MAX);
1113 if row >= sr0 && row <= er0 && col >= sc0 && col <= ec0 {
1114 to_visit.push(dep_id);
1115 break;
1116 }
1117 }
1118 }
1119 }
1120 }
1121
1122 while let Some(id) = to_visit.pop() {
1123 if !visited_for_propagation.insert(id) {
1124 continue; }
1126 affected.insert(id);
1127
1128 self.store.set_dirty(id, true);
1130
1131 let dependents = self.get_dependents(id);
1133 to_visit.extend(&dependents);
1134 }
1135
1136 self.dirty_vertices.extend(&affected);
1138
1139 affected.into_iter().collect()
1141 }
1142
1143 pub fn get_evaluation_vertices(&self) -> Vec<VertexId> {
1145 let mut combined = FxHashSet::default();
1146 combined.extend(&self.dirty_vertices);
1147 combined.extend(&self.volatile_vertices);
1148
1149 let mut result: Vec<VertexId> = combined
1150 .into_iter()
1151 .filter(|&id| {
1152 matches!(
1154 self.store.kind(id),
1155 VertexKind::FormulaScalar
1156 | VertexKind::FormulaArray
1157 | VertexKind::NamedScalar
1158 | VertexKind::NamedArray
1159 )
1160 })
1161 .collect();
1162 result.sort_unstable();
1163 result
1164 }
1165
1166 pub fn clear_dirty_flags(&mut self, vertices: &[VertexId]) {
1168 for &vertex_id in vertices {
1169 self.store.set_dirty(vertex_id, false);
1170 self.dirty_vertices.remove(&vertex_id);
1171 }
1172 }
1173
1174 pub fn clear_volatile_flags(&mut self) {
1176 self.volatile_vertices.clear();
1177 }
1178
1179 pub(crate) fn redirty_volatiles(&mut self) {
1181 let volatile_ids: Vec<VertexId> = self.volatile_vertices.iter().copied().collect();
1182 for id in volatile_ids {
1183 self.mark_dirty(id);
1184 }
1185 }
1186
1187 fn get_or_create_vertex(
1188 &mut self,
1189 addr: &CellRef,
1190 created_placeholders: &mut Vec<CellRef>,
1191 ) -> VertexId {
1192 if let Some(&vertex_id) = self.cell_to_vertex.get(addr) {
1193 return vertex_id;
1194 }
1195
1196 created_placeholders.push(*addr);
1197 let packed_coord = AbsCoord::new(addr.coord.row(), addr.coord.col());
1198 let vertex_id = self.store.allocate(packed_coord, addr.sheet_id, 0x00);
1199
1200 self.edges.add_vertex(packed_coord, vertex_id.0);
1202
1203 self.sheet_index_mut(addr.sheet_id)
1205 .add_vertex(packed_coord, vertex_id);
1206
1207 self.store.set_kind(vertex_id, VertexKind::Empty);
1208 self.cell_to_vertex.insert(*addr, vertex_id);
1209 vertex_id
1210 }
1211
1212 fn add_dependent_edges(&mut self, dependent: VertexId, dependencies: &[VertexId]) {
1213 self.edges.begin_batch();
1215
1216 let mut skip_deps: rustc_hash::FxHashSet<VertexId> = rustc_hash::FxHashSet::default();
1219 if self.pk_order.is_some()
1220 && let Some(mut pk) = self.pk_order.take()
1221 {
1222 pk.ensure_nodes(std::iter::once(dependent));
1223 pk.ensure_nodes(dependencies.iter().copied());
1224 {
1225 let adapter = GraphAdapter { g: self };
1226 for &dep_id in dependencies {
1227 match pk.try_add_edge(&adapter, dep_id, dependent) {
1228 Ok(_) => {}
1229 Err(_cycle) => {
1230 if self.config.pk_reject_cycle_edges {
1231 skip_deps.insert(dep_id);
1232 } else {
1233 pk.rebuild_full(&adapter);
1234 }
1235 }
1236 }
1237 }
1238 } self.pk_order = Some(pk);
1240 }
1241
1242 for &dep_id in dependencies {
1244 if self.config.pk_reject_cycle_edges && skip_deps.contains(&dep_id) {
1245 continue;
1246 }
1247 self.edges.add_edge(dependent, dep_id);
1248 #[cfg(test)]
1249 {
1250 self.instr.edges_added += 1;
1251 }
1252 }
1253
1254 self.edges.end_batch();
1255 }
1256
1257 fn add_dependent_edges_nobatch(&mut self, dependent: VertexId, dependencies: &[VertexId]) {
1259 let mut skip_deps: rustc_hash::FxHashSet<VertexId> = rustc_hash::FxHashSet::default();
1261 if self.pk_order.is_some()
1262 && let Some(mut pk) = self.pk_order.take()
1263 {
1264 pk.ensure_nodes(std::iter::once(dependent));
1265 pk.ensure_nodes(dependencies.iter().copied());
1266 {
1267 let adapter = GraphAdapter { g: self };
1268 for &dep_id in dependencies {
1269 match pk.try_add_edge(&adapter, dep_id, dependent) {
1270 Ok(_) => {}
1271 Err(_cycle) => {
1272 if self.config.pk_reject_cycle_edges {
1273 skip_deps.insert(dep_id);
1274 } else {
1275 pk.rebuild_full(&adapter);
1276 }
1277 }
1278 }
1279 }
1280 }
1281 self.pk_order = Some(pk);
1282 }
1283
1284 for &dep_id in dependencies {
1285 if self.config.pk_reject_cycle_edges && skip_deps.contains(&dep_id) {
1286 continue;
1287 }
1288 self.edges.add_edge(dependent, dep_id);
1289 #[cfg(test)]
1290 {
1291 self.instr.edges_added += 1;
1292 }
1293 }
1294 }
1295
1296 pub fn bulk_set_formulas<I>(&mut self, sheet: &str, items: I) -> Result<usize, ExcelError>
1298 where
1299 I: IntoIterator<Item = (u32, u32, ASTNode)>,
1300 {
1301 let collected: Vec<(u32, u32, ASTNode)> = items.into_iter().collect();
1302 if collected.is_empty() {
1303 return Ok(0);
1304 }
1305 let vol_flags: Vec<bool> = collected
1306 .iter()
1307 .map(|(_, _, ast)| self.is_ast_volatile(ast))
1308 .collect();
1309 self.bulk_set_formulas_with_volatility(sheet, collected, vol_flags)
1310 }
1311
1312 pub fn bulk_set_formulas_with_volatility(
1313 &mut self,
1314 sheet: &str,
1315 collected: Vec<(u32, u32, ASTNode)>,
1316 vol_flags: Vec<bool>,
1317 ) -> Result<usize, ExcelError> {
1318 use formualizer_parse::parser::CollectPolicy;
1319 let sheet_id = self.sheet_id_mut(sheet);
1320
1321 if collected.is_empty() {
1322 return Ok(0);
1323 }
1324
1325 let tiny_refs = collected.iter().map(|(r, c, ast)| (sheet, *r, *c, ast));
1327 let policy = CollectPolicy {
1328 expand_small_ranges: true,
1329 range_expansion_limit: self.config.range_expansion_limit,
1330 include_names: true,
1331 };
1332 let plan = crate::engine::plan::build_dependency_plan(
1333 &mut self.sheet_reg,
1334 tiny_refs,
1335 &policy,
1336 Some(&vol_flags),
1337 )?;
1338
1339 let mut created_placeholders: Vec<CellRef> = Vec::new();
1341
1342 let mut target_vids: Vec<VertexId> = Vec::with_capacity(plan.formula_targets.len());
1344 for (sid, pc) in &plan.formula_targets {
1345 let addr = CellRef::new(*sid, Coord::new(pc.row(), pc.col(), true, true));
1346 let vid = if let Some(&existing) = self.cell_to_vertex.get(&addr) {
1347 existing
1348 } else {
1349 self.get_or_create_vertex(&addr, &mut created_placeholders)
1350 };
1351 target_vids.push(vid);
1352 }
1353
1354 let mut dep_vids: Vec<VertexId> = Vec::with_capacity(plan.global_cells.len());
1356 for (sid, pc) in &plan.global_cells {
1357 let addr = CellRef::new(*sid, Coord::new(pc.row(), pc.col(), true, true));
1358 let vid = if let Some(&existing) = self.cell_to_vertex.get(&addr) {
1359 existing
1360 } else {
1361 self.get_or_create_vertex(&addr, &mut created_placeholders)
1362 };
1363 dep_vids.push(vid);
1364 }
1365
1366 let ast_ids = self
1368 .data_store
1369 .store_asts_batch(collected.iter().map(|(_, _, ast)| ast), &self.sheet_reg);
1370
1371 for (i, &tvid) in target_vids.iter().enumerate() {
1372 if self.vertex_formulas.contains_key(&tvid) {
1374 self.remove_dependent_edges(tvid);
1375 }
1376 self.store.set_kind(tvid, VertexKind::FormulaScalar);
1377 self.store.set_dirty(tvid, true);
1378 self.vertex_values.remove(&tvid);
1379 self.vertex_formulas.insert(tvid, ast_ids[i]);
1380 self.mark_volatile(tvid, vol_flags.get(i).copied().unwrap_or(false));
1381 }
1382
1383 self.edges.begin_batch();
1385 for (i, tvid) in target_vids.iter().copied().enumerate() {
1386 let mut deps: Vec<VertexId> = Vec::new();
1387
1388 if let Some(indices) = plan.per_formula_cells.get(i) {
1390 deps.reserve(indices.len());
1391 for &idx in indices {
1392 if let Some(vid) = dep_vids.get(idx as usize) {
1393 deps.push(*vid);
1394 }
1395 }
1396 }
1397
1398 if let Some(names) = plan.per_formula_names.get(i)
1399 && !names.is_empty()
1400 {
1401 let mut name_vertices = Vec::new();
1402 let formula_sheet = plan
1403 .formula_targets
1404 .get(i)
1405 .map(|(sid, _)| *sid)
1406 .unwrap_or(sheet_id);
1407 for name in names {
1408 if let Some(named) = self.resolve_name_entry(name, formula_sheet) {
1409 deps.push(named.vertex);
1410 name_vertices.push(named.vertex);
1411 } else if let Some(source) = self.resolve_source_scalar_entry(name) {
1412 deps.push(source.vertex);
1413 } else {
1414 self.record_pending_name_reference(formula_sheet, name, tvid);
1415 }
1416 }
1417 if !name_vertices.is_empty() {
1418 self.attach_vertex_to_names(tvid, &name_vertices);
1419 }
1420 }
1421
1422 if let Some(tables) = plan.per_formula_tables.get(i)
1423 && !tables.is_empty()
1424 {
1425 for table_name in tables {
1426 if let Some(table) = self.resolve_table_entry(table_name) {
1427 deps.push(table.vertex);
1428 } else if let Some(source) = self.resolve_source_table_entry(table_name) {
1429 deps.push(source.vertex);
1430 }
1431 }
1432 }
1433
1434 if !deps.is_empty() {
1435 self.add_dependent_edges_nobatch(tvid, &deps);
1436 }
1437
1438 if let Some(rks) = plan.per_formula_ranges.get(i) {
1440 self.add_range_deps_from_keys(tvid, rks, sheet_id);
1441 }
1442 }
1443 self.edges.end_batch();
1444
1445 Ok(collected.len())
1446 }
1447
1448 pub fn add_dependency_edge(&mut self, dependent: VertexId, dependency: VertexId) {
1450 if dependent == dependency {
1451 return;
1452 }
1453 if self.pk_order.is_some()
1455 && let Some(mut pk) = self.pk_order.take()
1456 {
1457 pk.ensure_nodes(std::iter::once(dependent));
1458 pk.ensure_nodes(std::iter::once(dependency));
1459 let adapter = GraphAdapter { g: self };
1460 if pk.try_add_edge(&adapter, dependency, dependent).is_err() {
1461 pk.rebuild_full(&adapter);
1463 }
1464 self.pk_order = Some(pk);
1465 }
1466 self.edges.add_edge(dependent, dependency);
1467 self.store.set_dirty(dependent, true);
1468 self.dirty_vertices.insert(dependent);
1469 }
1470
1471 fn remove_dependent_edges(&mut self, vertex: VertexId) {
1472 let dependencies = self.edges.out_edges(vertex);
1474
1475 self.edges.begin_batch();
1476 if self.pk_order.is_some()
1477 && let Some(mut pk) = self.pk_order.take()
1478 {
1479 for dep in &dependencies {
1480 pk.remove_edge(*dep, vertex);
1481 }
1482 self.pk_order = Some(pk);
1483 }
1484 for dep in dependencies {
1485 self.edges.remove_edge(vertex, dep);
1486 }
1487 self.edges.end_batch();
1488
1489 if let Some(old_ranges) = self.formula_to_range_deps.remove(&vertex) {
1491 let old_sheet_id = self.store.sheet_id(vertex);
1492
1493 for range in &old_ranges {
1494 let sheet_id = match range.sheet {
1495 SharedSheetLocator::Id(id) => id,
1496 _ => old_sheet_id,
1497 };
1498 let s_row = range.start_row.map(|b| b.index);
1499 let e_row = range.end_row.map(|b| b.index);
1500 let s_col = range.start_col.map(|b| b.index);
1501 let e_col = range.end_col.map(|b| b.index);
1502
1503 let mut keys_to_clean = FxHashSet::default();
1504
1505 let col_stripes = (s_row.is_none() && e_row.is_none())
1506 || (s_col.is_some() && e_col.is_some() && (s_row.is_none() || e_row.is_none()));
1507 let row_stripes = (s_col.is_none() && e_col.is_none())
1508 || (s_row.is_some() && e_row.is_some() && (s_col.is_none() || e_col.is_none()));
1509
1510 if col_stripes && !row_stripes {
1511 let sc = s_col.unwrap_or(0);
1512 let ec = e_col.unwrap_or(sc);
1513 for col in sc..=ec {
1514 keys_to_clean.insert(StripeKey {
1515 sheet_id,
1516 stripe_type: StripeType::Column,
1517 index: col,
1518 });
1519 }
1520 } else if row_stripes && !col_stripes {
1521 let sr = s_row.unwrap_or(0);
1522 let er = e_row.unwrap_or(sr);
1523 for row in sr..=er {
1524 keys_to_clean.insert(StripeKey {
1525 sheet_id,
1526 stripe_type: StripeType::Row,
1527 index: row,
1528 });
1529 }
1530 } else {
1531 let start_row = s_row.unwrap_or(0);
1532 let start_col = s_col.unwrap_or(0);
1533 let end_row = e_row.unwrap_or(start_row);
1534 let end_col = e_col.unwrap_or(start_col);
1535
1536 let height = end_row.saturating_sub(start_row) + 1;
1537 let width = end_col.saturating_sub(start_col) + 1;
1538
1539 if self.config.enable_block_stripes && height > 1 && width > 1 {
1540 let start_block_row = start_row / BLOCK_H;
1541 let end_block_row = end_row / BLOCK_H;
1542 let start_block_col = start_col / BLOCK_W;
1543 let end_block_col = end_col / BLOCK_W;
1544
1545 for block_row in start_block_row..=end_block_row {
1546 for block_col in start_block_col..=end_block_col {
1547 keys_to_clean.insert(StripeKey {
1548 sheet_id,
1549 stripe_type: StripeType::Block,
1550 index: block_index(block_row * BLOCK_H, block_col * BLOCK_W),
1551 });
1552 }
1553 }
1554 } else if height > width {
1555 for col in start_col..=end_col {
1556 keys_to_clean.insert(StripeKey {
1557 sheet_id,
1558 stripe_type: StripeType::Column,
1559 index: col,
1560 });
1561 }
1562 } else {
1563 for row in start_row..=end_row {
1564 keys_to_clean.insert(StripeKey {
1565 sheet_id,
1566 stripe_type: StripeType::Row,
1567 index: row,
1568 });
1569 }
1570 }
1571 }
1572
1573 for key in keys_to_clean {
1574 if let Some(dependents) = self.stripe_to_dependents.get_mut(&key) {
1575 dependents.remove(&vertex);
1576 if dependents.is_empty() {
1577 self.stripe_to_dependents.remove(&key);
1578 #[cfg(test)]
1579 {
1580 self.instr.stripe_removes += 1;
1581 }
1582 }
1583 }
1584 }
1585 }
1586 }
1587 }
1588
1589 pub(crate) fn update_vertex_value(&mut self, vertex_id: VertexId, value: LiteralValue) {
1595 let value_ref = self.data_store.store_value(value);
1596 self.vertex_values.insert(vertex_id, value_ref);
1597 }
1598
1599 pub fn plan_spill_region(
1601 &self,
1602 anchor: VertexId,
1603 target_cells: &[CellRef],
1604 ) -> Result<(), ExcelError> {
1605 use formualizer_common::{ExcelErrorExtra, ExcelErrorKind};
1606 let (expected_rows, expected_cols) = if target_cells.is_empty() {
1608 (0u32, 0u32)
1609 } else {
1610 let mut min_r = u32::MAX;
1611 let mut max_r = 0u32;
1612 let mut min_c = u32::MAX;
1613 let mut max_c = 0u32;
1614 for cell in target_cells {
1615 let r = cell.coord.row();
1616 let c = cell.coord.col();
1617 if r < min_r {
1618 min_r = r;
1619 }
1620 if r > max_r {
1621 max_r = r;
1622 }
1623 if c < min_c {
1624 min_c = c;
1625 }
1626 if c > max_c {
1627 max_c = c;
1628 }
1629 }
1630 (
1631 max_r.saturating_sub(min_r).saturating_add(1),
1632 max_c.saturating_sub(min_c).saturating_add(1),
1633 )
1634 };
1635 for cell in target_cells {
1637 let owned_by_anchor = match self.spill_cell_to_anchor.get(cell) {
1639 Some(&existing_anchor) if existing_anchor == anchor => true,
1640 Some(_other) => {
1641 return Err(ExcelError::new(ExcelErrorKind::Spill)
1642 .with_message("BlockedBySpill")
1643 .with_extra(ExcelErrorExtra::Spill {
1644 expected_rows,
1645 expected_cols,
1646 }));
1647 }
1648 None => false,
1649 };
1650
1651 if owned_by_anchor {
1652 continue;
1653 }
1654
1655 if let Some(&vid) = self.cell_to_vertex.get(cell)
1657 && vid != anchor
1658 {
1659 match self.store.kind(vid) {
1661 VertexKind::FormulaScalar | VertexKind::FormulaArray => {
1662 return Err(ExcelError::new(ExcelErrorKind::Spill)
1663 .with_message("BlockedByFormula")
1664 .with_extra(ExcelErrorExtra::Spill {
1665 expected_rows,
1666 expected_cols,
1667 }));
1668 }
1669 _ => {
1670 if let Some(vref) = self.vertex_values.get(&vid) {
1672 let v = self.data_store.retrieve_value(*vref);
1673 if !matches!(v, LiteralValue::Empty) {
1674 return Err(ExcelError::new(ExcelErrorKind::Spill)
1675 .with_message("BlockedByValue")
1676 .with_extra(ExcelErrorExtra::Spill {
1677 expected_rows,
1678 expected_cols,
1679 }));
1680 }
1681 }
1682 }
1683 }
1684 }
1685 }
1686 Ok(())
1687 }
1688
1689 pub fn commit_spill_region_atomic_with_fault(
1696 &mut self,
1697 anchor: VertexId,
1698 target_cells: Vec<CellRef>,
1699 values: Vec<Vec<LiteralValue>>,
1700 fault_after_ops: Option<usize>,
1701 ) -> Result<(), ExcelError> {
1702 use rustc_hash::FxHashSet;
1703
1704 let prev_cells = self
1706 .spill_anchor_to_cells
1707 .get(&anchor)
1708 .cloned()
1709 .unwrap_or_default();
1710 let new_set: FxHashSet<CellRef> = target_cells.iter().copied().collect();
1711 let prev_set: FxHashSet<CellRef> = prev_cells.iter().copied().collect();
1712
1713 #[derive(Clone)]
1715 struct Op {
1716 sheet: String,
1717 row: u32,
1718 col: u32,
1719 new_value: LiteralValue,
1720 }
1721 let mut ops: Vec<Op> = Vec::new();
1722
1723 for cell in prev_cells.iter() {
1725 if !new_set.contains(cell) {
1726 let sheet = self.sheet_name(cell.sheet_id).to_string();
1727 ops.push(Op {
1728 sheet,
1729 row: cell.coord.row(),
1730 col: cell.coord.col(),
1731 new_value: LiteralValue::Empty,
1732 });
1733 }
1734 }
1735
1736 if !target_cells.is_empty() {
1738 let first = target_cells.first().copied().unwrap();
1739 let row0 = first.coord.row();
1740 let col0 = first.coord.col();
1741 let sheet = self.sheet_name(first.sheet_id).to_string();
1742 for (r_off, row_vals) in values.iter().enumerate() {
1743 for (c_off, v) in row_vals.iter().enumerate() {
1744 ops.push(Op {
1745 sheet: sheet.clone(),
1746 row: row0 + r_off as u32,
1747 col: col0 + c_off as u32,
1748 new_value: v.clone(),
1749 });
1750 }
1751 }
1752 }
1753
1754 #[derive(Clone)]
1756 struct OldVal {
1757 present: bool,
1758 value: LiteralValue,
1759 }
1760 let mut old_values: Vec<((String, u32, u32), OldVal)> = Vec::with_capacity(ops.len());
1761
1762 for op in &ops {
1764 let old = self
1766 .get_cell_value(&op.sheet, op.row + 1, op.col + 1)
1767 .unwrap_or(LiteralValue::Empty);
1768 let present = true; old_values.push((
1770 (op.sheet.clone(), op.row, op.col),
1771 OldVal {
1772 present,
1773 value: old,
1774 },
1775 ));
1776 }
1777
1778 for (applied, op) in ops.iter().enumerate() {
1780 if let Some(n) = fault_after_ops
1781 && applied == n
1782 {
1783 for idx in (0..applied).rev() {
1784 let ((ref sheet, row, col), ref old) = old_values[idx];
1785 let _ = self.set_cell_value(sheet, row + 1, col + 1, old.value.clone());
1786 }
1787 return Err(ExcelError::new(ExcelErrorKind::Error)
1788 .with_message("Injected persistence fault during spill commit"));
1789 }
1790 let _ = self.set_cell_value(&op.sheet, op.row + 1, op.col + 1, op.new_value.clone());
1791 }
1792
1793 for cell in prev_cells.iter() {
1796 if !new_set.contains(cell) {
1797 self.spill_cell_to_anchor.remove(cell);
1798 }
1799 }
1800 for cell in &target_cells {
1802 self.spill_cell_to_anchor.insert(*cell, anchor);
1803 }
1804 self.spill_anchor_to_cells.insert(anchor, target_cells);
1805 Ok(())
1806 }
1807
1808 pub(crate) fn spill_cells_for_anchor(&self, anchor: VertexId) -> Option<&[CellRef]> {
1809 self.spill_anchor_to_cells
1810 .get(&anchor)
1811 .map(|v| v.as_slice())
1812 }
1813
1814 pub fn clear_spill_region(&mut self, anchor: VertexId) {
1816 if let Some(cells) = self.spill_anchor_to_cells.remove(&anchor) {
1817 for cell in cells {
1818 let sheet = self.sheet_name(cell.sheet_id).to_string();
1819 let _ = self.set_cell_value(
1820 &sheet,
1821 cell.coord.row() + 1,
1822 cell.coord.col() + 1,
1823 LiteralValue::Empty,
1824 );
1825 self.spill_cell_to_anchor.remove(&cell);
1826 }
1827 }
1828 }
1829
1830 pub(crate) fn vertex_exists(&self, vertex_id: VertexId) -> bool {
1832 if vertex_id.0 < FIRST_NORMAL_VERTEX {
1833 return false;
1834 }
1835 let index = (vertex_id.0 - FIRST_NORMAL_VERTEX) as usize;
1836 index < self.store.len()
1837 }
1838
1839 pub(crate) fn get_vertex_kind(&self, vertex_id: VertexId) -> VertexKind {
1841 self.store.kind(vertex_id)
1842 }
1843
1844 pub(crate) fn get_vertex_sheet_id(&self, vertex_id: VertexId) -> SheetId {
1846 self.store.sheet_id(vertex_id)
1847 }
1848
1849 pub fn get_formula_id(&self, vertex_id: VertexId) -> Option<AstNodeId> {
1850 self.vertex_formulas.get(&vertex_id).copied()
1851 }
1852
1853 pub fn get_formula_id_and_volatile(&self, vertex_id: VertexId) -> Option<(AstNodeId, bool)> {
1854 let ast_id = self.get_formula_id(vertex_id)?;
1855 Some((ast_id, self.is_volatile(vertex_id)))
1856 }
1857
1858 pub fn get_formula_node(&self, vertex_id: VertexId) -> Option<&super::arena::AstNodeData> {
1859 let ast_id = self.get_formula_id(vertex_id)?;
1860 self.data_store.get_node(ast_id)
1861 }
1862
1863 pub fn get_formula_node_and_volatile(
1864 &self,
1865 vertex_id: VertexId,
1866 ) -> Option<(&super::arena::AstNodeData, bool)> {
1867 let (ast_id, vol) = self.get_formula_id_and_volatile(vertex_id)?;
1868 let node = self.data_store.get_node(ast_id)?;
1869 Some((node, vol))
1870 }
1871
1872 pub fn get_formula(&self, vertex_id: VertexId) -> Option<ASTNode> {
1876 let ast_id = self.get_formula_id(vertex_id)?;
1877 self.data_store.retrieve_ast(ast_id, &self.sheet_reg)
1878 }
1879
1880 pub fn get_value(&self, vertex_id: VertexId) -> Option<LiteralValue> {
1882 self.vertex_values
1883 .get(&vertex_id)
1884 .map(|&value_ref| self.data_store.retrieve_value(value_ref))
1885 }
1886
1887 pub(crate) fn get_cell_ref(&self, vertex_id: VertexId) -> Option<CellRef> {
1889 let packed_coord = self.store.coord(vertex_id);
1890 let sheet_id = self.store.sheet_id(vertex_id);
1891 let coord = Coord::new(packed_coord.row(), packed_coord.col(), true, true);
1892 Some(CellRef::new(sheet_id, coord))
1893 }
1894
1895 pub(crate) fn make_cell_ref_internal(&self, sheet_id: SheetId, row: u32, col: u32) -> CellRef {
1897 let coord = Coord::new(row, col, true, true);
1898 CellRef::new(sheet_id, coord)
1899 }
1900
1901 pub fn make_cell_ref(&self, sheet_name: &str, row: u32, col: u32) -> CellRef {
1903 let sheet_id = self.sheet_reg.get_id(sheet_name).unwrap_or(0);
1904 let coord = Coord::from_excel(row, col, true, true);
1905 CellRef::new(sheet_id, coord)
1906 }
1907
1908 pub(crate) fn is_dirty(&self, vertex_id: VertexId) -> bool {
1910 self.store.is_dirty(vertex_id)
1911 }
1912
1913 pub(crate) fn is_volatile(&self, vertex_id: VertexId) -> bool {
1915 self.store.is_volatile(vertex_id)
1916 }
1917
1918 pub fn get_vertex_id_for_address(&self, addr: &CellRef) -> Option<&VertexId> {
1920 self.cell_to_vertex.get(addr)
1921 }
1922
1923 #[cfg(test)]
1924 pub fn cell_to_vertex(&self) -> &FxHashMap<CellRef, VertexId> {
1925 &self.cell_to_vertex
1926 }
1927
1928 pub(crate) fn get_dependencies(&self, vertex_id: VertexId) -> Vec<VertexId> {
1930 self.edges.out_edges(vertex_id)
1931 }
1932
1933 pub(crate) fn has_self_loop(&self, vertex_id: VertexId) -> bool {
1935 self.edges.out_edges(vertex_id).contains(&vertex_id)
1936 }
1937
1938 pub(crate) fn get_dependents(&self, vertex_id: VertexId) -> Vec<VertexId> {
1941 if self.edges.delta_size() > 0 {
1944 let mut dependents = Vec::new();
1946 for (&_addr, &vid) in &self.cell_to_vertex {
1947 let out_edges = self.edges.out_edges(vid);
1948 if out_edges.contains(&vertex_id) {
1949 dependents.push(vid);
1950 }
1951 }
1952 for named in self.named_ranges.values() {
1953 let vid = named.vertex;
1954 let out_edges = self.edges.out_edges(vid);
1955 if out_edges.contains(&vertex_id) {
1956 dependents.push(vid);
1957 }
1958 }
1959 for named in self.sheet_named_ranges.values() {
1960 let vid = named.vertex;
1961 let out_edges = self.edges.out_edges(vid);
1962 if out_edges.contains(&vertex_id) {
1963 dependents.push(vid);
1964 }
1965 }
1966 dependents
1967 } else {
1968 self.edges.in_edges(vertex_id).to_vec()
1970 }
1971 }
1972
1973 #[doc(hidden)]
1977 pub fn snapshot_vertex(&self, id: VertexId) -> crate::engine::VertexSnapshot {
1978 let coord = self.store.coord(id);
1979 let sheet_id = self.store.sheet_id(id);
1980 let kind = self.store.kind(id);
1981 let flags = self.store.flags(id);
1982
1983 let value_ref = self.vertex_values.get(&id).copied();
1985 let formula_ref = self.vertex_formulas.get(&id).copied();
1986
1987 let out_edges = self.get_dependencies(id);
1989
1990 crate::engine::VertexSnapshot {
1991 coord,
1992 sheet_id,
1993 kind,
1994 flags,
1995 value_ref,
1996 formula_ref,
1997 out_edges,
1998 }
1999 }
2000
2001 #[doc(hidden)]
2003 pub fn remove_all_edges(&mut self, id: VertexId) {
2004 self.edges.begin_batch();
2006
2007 self.remove_dependent_edges(id);
2009
2010 self.edges.rebuild();
2013
2014 let dependents = self.get_dependents(id);
2016 if self.pk_order.is_some()
2017 && let Some(mut pk) = self.pk_order.take()
2018 {
2019 for dependent in &dependents {
2020 pk.remove_edge(id, *dependent);
2021 }
2022 self.pk_order = Some(pk);
2023 }
2024 for dependent in dependents {
2025 self.edges.remove_edge(dependent, id);
2026 }
2027
2028 self.edges.end_batch();
2030 }
2031
2032 #[doc(hidden)]
2034 pub fn mark_as_ref_error(&mut self, id: VertexId) {
2035 let error = LiteralValue::Error(ExcelError::new(ExcelErrorKind::Ref));
2036 let value_ref = self.data_store.store_value(error);
2037 self.vertex_values.insert(id, value_ref);
2038 self.store.set_dirty(id, true);
2039 self.dirty_vertices.insert(id);
2040 }
2041
2042 pub fn is_ref_error(&self, id: VertexId) -> bool {
2044 if let Some(value_ref) = self.vertex_values.get(&id) {
2045 let value = self.data_store.retrieve_value(*value_ref);
2046 if let LiteralValue::Error(err) = value {
2047 return err.kind == ExcelErrorKind::Ref;
2048 }
2049 }
2050 false
2051 }
2052
2053 #[doc(hidden)]
2055 pub fn mark_dependents_dirty(&mut self, id: VertexId) {
2056 let dependents = self.get_dependents(id);
2057 for dep_id in dependents {
2058 self.store.set_dirty(dep_id, true);
2059 self.dirty_vertices.insert(dep_id);
2060 }
2061 }
2062
2063 #[doc(hidden)]
2065 pub fn mark_volatile(&mut self, id: VertexId, volatile: bool) {
2066 self.store.set_volatile(id, volatile);
2067 if volatile {
2068 self.volatile_vertices.insert(id);
2069 } else {
2070 self.volatile_vertices.remove(&id);
2071 }
2072 }
2073
2074 #[doc(hidden)]
2076 pub fn set_coord(&mut self, id: VertexId, coord: AbsCoord) {
2077 self.store.set_coord(id, coord);
2078 }
2079
2080 #[doc(hidden)]
2082 pub fn update_edge_coord(&mut self, id: VertexId, coord: AbsCoord) {
2083 self.edges.update_coord(id, coord);
2084 }
2085
2086 #[doc(hidden)]
2088 pub fn mark_deleted(&mut self, id: VertexId, deleted: bool) {
2089 self.store.mark_deleted(id, deleted);
2090 }
2091
2092 #[doc(hidden)]
2094 pub fn set_kind(&mut self, id: VertexId, kind: VertexKind) {
2095 self.store.set_kind(id, kind);
2096 }
2097
2098 #[doc(hidden)]
2100 pub fn set_dirty(&mut self, id: VertexId, dirty: bool) {
2101 self.store.set_dirty(id, dirty);
2102 if dirty {
2103 self.dirty_vertices.insert(id);
2104 } else {
2105 self.dirty_vertices.remove(&id);
2106 }
2107 }
2108
2109 #[cfg(test)]
2111 pub(crate) fn get_kind(&self, id: VertexId) -> VertexKind {
2112 self.store.kind(id)
2113 }
2114
2115 #[cfg(test)]
2117 pub(crate) fn get_flags(&self, id: VertexId) -> u8 {
2118 self.store.flags(id)
2119 }
2120
2121 #[cfg(test)]
2123 pub(crate) fn is_deleted(&self, id: VertexId) -> bool {
2124 self.store.is_deleted(id)
2125 }
2126
2127 #[doc(hidden)]
2129 pub fn rebuild_edges(&mut self) {
2130 self.edges.rebuild();
2131 }
2132
2133 #[doc(hidden)]
2135 pub fn edges_delta_size(&self) -> usize {
2136 self.edges.delta_size()
2137 }
2138
2139 pub fn get_vertex_for_cell(&self, addr: &CellRef) -> Option<VertexId> {
2141 self.cell_to_vertex.get(addr).copied()
2142 }
2143
2144 pub fn get_coord(&self, id: VertexId) -> AbsCoord {
2146 self.store.coord(id)
2147 }
2148
2149 pub fn get_sheet_id(&self, id: VertexId) -> SheetId {
2151 self.store.sheet_id(id)
2152 }
2153
2154 pub fn vertices_in_sheet(&self, sheet_id: SheetId) -> impl Iterator<Item = VertexId> + '_ {
2156 self.store
2157 .all_vertices()
2158 .filter(move |&id| self.vertex_exists(id) && self.store.sheet_id(id) == sheet_id)
2159 }
2160
2161 pub fn vertex_has_formula(&self, id: VertexId) -> bool {
2163 self.vertex_formulas.contains_key(&id)
2164 }
2165
2166 pub fn vertices_with_formulas(&self) -> impl Iterator<Item = VertexId> + '_ {
2168 self.vertex_formulas.keys().copied()
2169 }
2170
2171 pub fn update_vertex_formula(&mut self, id: VertexId, ast: ASTNode) -> Result<(), ExcelError> {
2173 let sheet_id = self.store.sheet_id(id);
2175
2176 let has_ref_marker = ast.get_dependencies().into_iter().any(|r| {
2180 matches!(
2181 r,
2182 ReferenceType::Cell { sheet: Some(s), .. }
2183 | ReferenceType::Range { sheet: Some(s), .. } if s == "#REF"
2184 )
2185 });
2186 if has_ref_marker {
2187 let ast_id = self.data_store.store_ast(&ast, &self.sheet_reg);
2189 self.vertex_formulas.insert(id, ast_id);
2190 self.mark_as_ref_error(id);
2191 self.store.set_kind(id, VertexKind::FormulaScalar);
2192 return Ok(());
2193 }
2194
2195 let (new_dependencies, new_range_dependencies, _, named_dependencies) =
2197 self.extract_dependencies(&ast, sheet_id)?;
2198
2199 self.remove_dependent_edges(id);
2201 self.detach_vertex_from_names(id);
2202
2203 let ast_id = self.data_store.store_ast(&ast, &self.sheet_reg);
2205 self.vertex_formulas.insert(id, ast_id);
2206
2207 self.add_dependent_edges(id, &new_dependencies);
2209 self.add_range_dependent_edges(id, &new_range_dependencies, sheet_id);
2210
2211 if !named_dependencies.is_empty() {
2212 self.attach_vertex_to_names(id, &named_dependencies);
2213 }
2214
2215 self.store.set_kind(id, VertexKind::FormulaScalar);
2217
2218 Ok(())
2219 }
2220
2221 pub fn mark_vertex_dirty(&mut self, vertex_id: VertexId) {
2223 self.store.set_dirty(vertex_id, true);
2224 self.dirty_vertices.insert(vertex_id);
2225 }
2226
2227 pub fn update_cell_mapping(
2229 &mut self,
2230 id: VertexId,
2231 old_addr: Option<CellRef>,
2232 new_addr: CellRef,
2233 ) {
2234 if let Some(old) = old_addr {
2236 self.cell_to_vertex.remove(&old);
2237 }
2238 self.cell_to_vertex.insert(new_addr, id);
2240 }
2241
2242 pub fn remove_cell_mapping(&mut self, addr: &CellRef) {
2244 self.cell_to_vertex.remove(addr);
2245 }
2246
2247 pub fn get_cell_ref_for_vertex(&self, id: VertexId) -> Option<CellRef> {
2249 let coord = self.store.coord(id);
2250 let sheet_id = self.store.sheet_id(id);
2251 let cell_ref = CellRef::new(sheet_id, Coord::new(coord.row(), coord.col(), true, true));
2253 if self.cell_to_vertex.get(&cell_ref) == Some(&id) {
2255 Some(cell_ref)
2256 } else {
2257 None
2258 }
2259 }
2260
2261 }
2263
2264