1use super::*;
2use crate::engine::used_extent::{ExtentPolicy, OpenRangeBounds, resolve_used_extent};
3use formualizer_common::LiteralValue;
4use formualizer_parse::parser::{ASTNode, ASTNodeType, ReferenceType};
5
6#[derive(Clone, Copy, PartialEq, Eq)]
7enum RangeSelfUse {
8 NoMatch,
9 Excluded,
10 IncludedOrUnknown,
11}
12
13impl RangeSelfUse {
14 fn merge(self, other: Self) -> Self {
15 match (self, other) {
16 (Self::IncludedOrUnknown, _) | (_, Self::IncludedOrUnknown) => Self::IncludedOrUnknown,
17 (Self::Excluded, _) | (_, Self::Excluded) => Self::Excluded,
18 _ => Self::NoMatch,
19 }
20 }
21}
22
23impl DependencyGraph {
24 pub(crate) fn compressed_range_dependents_intersecting_deleted_rows(
25 &self,
26 sheet_id: SheetId,
27 start_row: u32,
28 end_row: u32,
29 ) -> Vec<VertexId> {
30 self.formula_to_range_deps
31 .iter()
32 .filter_map(|(&dependent, ranges)| {
33 ranges
34 .iter()
35 .any(|range| {
36 let range_sheet_id = self
41 .sheet_reg
42 .resolve_locator(&range.sheet, self.get_vertex_sheet_id(dependent))
43 .unwrap_or(sheet_id);
44 let range_start = range.start_row.map(|bound| bound.index).unwrap_or(0);
45 let range_end = range.end_row.map(|bound| bound.index).unwrap_or(u32::MAX);
46 range_sheet_id == sheet_id
47 && range_start <= end_row
48 && range_end >= start_row
49 })
50 .then_some(dependent)
51 })
52 .collect()
53 }
54
55 pub(crate) fn compressed_range_dependents_intersecting_deleted_columns(
56 &self,
57 sheet_id: SheetId,
58 start_col: u32,
59 end_col: u32,
60 ) -> Vec<VertexId> {
61 self.formula_to_range_deps
62 .iter()
63 .filter_map(|(&dependent, ranges)| {
64 ranges
65 .iter()
66 .any(|range| {
67 let range_sheet_id = self
69 .sheet_reg
70 .resolve_locator(&range.sheet, self.get_vertex_sheet_id(dependent))
71 .unwrap_or(sheet_id);
72 let range_start = range.start_col.map(|bound| bound.index).unwrap_or(0);
73 let range_end = range.end_col.map(|bound| bound.index).unwrap_or(u32::MAX);
74 range_sheet_id == sheet_id
75 && range_start <= end_col
76 && range_end >= start_col
77 })
78 .then_some(dependent)
79 })
80 .collect()
81 }
82
83 pub(crate) fn visit_range_dependents_covering_bounded(
92 &self,
93 sheet_id: SheetId,
94 row0: u32,
95 col0: u32,
96 remaining_work: &mut u64,
97 visitor: &mut dyn FnMut(VertexId) -> bool,
98 ) -> bool {
99 if self.stripe_to_dependents.is_empty() {
100 return true;
101 }
102
103 let mut seen = FxHashSet::default();
104 let keys = [
105 StripeKey {
106 sheet_id,
107 stripe_type: StripeType::Column,
108 index: col0,
109 },
110 StripeKey {
111 sheet_id,
112 stripe_type: StripeType::Row,
113 index: row0,
114 },
115 StripeKey {
116 sheet_id,
117 stripe_type: StripeType::Block,
118 index: block_index(row0, col0),
119 },
120 ];
121
122 for key in keys {
123 if key.stripe_type == StripeType::Block && !self.config.enable_block_stripes {
124 continue;
125 }
126 let Some(candidates) = self.stripe_to_dependents.get(&key) else {
127 continue;
128 };
129 for &dependent in candidates {
130 if *remaining_work == 0 {
131 return false;
132 }
133 *remaining_work -= 1;
134 if !seen.insert(dependent) {
135 continue;
136 }
137 let Some(ranges) = self.formula_to_range_deps.get(&dependent) else {
138 continue;
139 };
140 let mut covered = false;
141 for range in ranges {
142 if *remaining_work == 0 {
143 return false;
144 }
145 *remaining_work -= 1;
146 let range_sheet = self
150 .sheet_reg
151 .resolve_locator(&range.sheet, self.get_vertex_sheet_id(dependent))
152 .unwrap_or(sheet_id);
153 if range_sheet != sheet_id {
154 continue;
155 }
156 let start_row = range.start_row.map(|bound| bound.index).unwrap_or(0);
157 let end_row = range.end_row.map(|bound| bound.index).unwrap_or(u32::MAX);
158 let start_col = range.start_col.map(|bound| bound.index).unwrap_or(0);
159 let end_col = range.end_col.map(|bound| bound.index).unwrap_or(u32::MAX);
160 if start_row <= row0 && row0 <= end_row && start_col <= col0 && col0 <= end_col
161 {
162 covered = true;
163 break;
164 }
165 }
166 if covered && !visitor(dependent) {
167 return false;
168 }
169 }
170 }
171 true
172 }
173
174 pub fn add_range_edges(
176 &mut self,
177 dependent: VertexId,
178 ranges: &[SharedRangeRef<'static>],
179 current_sheet_id: SheetId,
180 ) {
181 self.add_range_dependent_edges(dependent, ranges, current_sheet_id);
182 }
183
184 pub fn get_range_dependencies(
188 &self,
189 vertex: VertexId,
190 ) -> Option<&Vec<SharedRangeRef<'static>>> {
191 self.formula_to_range_deps.get(&vertex)
192 }
193
194 #[cfg(test)]
195 pub(crate) fn formula_to_range_deps(
196 &self,
197 ) -> &FxHashMap<VertexId, Vec<SharedRangeRef<'static>>> {
198 &self.formula_to_range_deps
199 }
200
201 #[cfg(test)]
202 pub(crate) fn stripe_to_dependents(&self) -> &FxHashMap<StripeKey, FxHashSet<VertexId>> {
203 &self.stripe_to_dependents
204 }
205
206 fn range_region_contains_self(
213 &self,
214 dependent: VertexId,
215 sheet_id: SheetId,
216 s_row: Option<u32>,
217 e_row: Option<u32>,
218 s_col: Option<u32>,
219 e_col: Option<u32>,
220 ) -> bool {
221 if self.store.sheet_id(dependent) != sheet_id {
222 return false;
223 }
224 let coord = self.store.coord(dependent);
225 let r0 = coord.row();
226 let c0 = coord.col();
227 s_row.is_none_or(|s| r0 >= s)
228 && e_row.is_none_or(|e| r0 <= e)
229 && s_col.is_none_or(|s| c0 >= s)
230 && e_col.is_none_or(|e| c0 <= e)
231 }
232
233 fn record_self_loop(&mut self, vertex: VertexId) {
236 if !self.has_self_loop(vertex) {
237 self.edges.add_edge(vertex, vertex);
238 }
239 }
240
241 pub(crate) fn compressed_range_resolved_bounds(
242 &self,
243 sheet: SheetId,
244 range: (Option<u32>, Option<u32>, Option<u32>, Option<u32>),
245 ) -> Option<(u32, u32, u32, u32)> {
246 let (start_row, end_row, start_col, end_col) = range;
247 let extent = resolve_used_extent(
248 OpenRangeBounds {
249 start_row,
250 start_column: start_col,
251 end_row,
252 end_column: end_col,
253 },
254 ExtentPolicy::GraphCompat {
255 fallback_row: self.config.max_open_ended_rows.saturating_sub(1),
256 fallback_column: self.config.max_open_ended_cols.saturating_sub(1),
257 },
258 |first, last| self.used_row_bounds_for_columns(sheet, first, last),
259 |first, last| self.used_col_bounds_for_rows(sheet, first, last),
260 )?;
261 Some((
262 extent.start_row,
263 extent.end_row,
264 extent.start_column,
265 extent.end_column,
266 ))
267 }
268
269 fn compressed_range_self_use(
276 &self,
277 dependent: VertexId,
278 range_sheet: SheetId,
279 range: (Option<u32>, Option<u32>, Option<u32>, Option<u32>),
280 ) -> RangeSelfUse {
281 let Some(ast) = self.get_formula(dependent) else {
282 return RangeSelfUse::IncludedOrUnknown;
283 };
284
285 fn static_index(node: &ASTNode) -> Option<i64> {
286 match &node.node_type {
287 ASTNodeType::Literal(LiteralValue::Int(value)) => Some(*value),
288 ASTNodeType::Literal(LiteralValue::Number(value)) if value.is_finite() => {
289 Some(*value as i64)
290 }
291 ASTNodeType::UnaryOp { op, expr } if op == "+" => static_index(expr),
292 ASTNodeType::UnaryOp { op, expr } if op == "-" => static_index(expr)?.checked_neg(),
293 _ => None,
294 }
295 }
296
297 fn matching_range(
298 graph: &DependencyGraph,
299 node: &ASTNode,
300 dependent: VertexId,
301 range_sheet: SheetId,
302 range: (Option<u32>, Option<u32>, Option<u32>, Option<u32>),
303 ) -> bool {
304 let ASTNodeType::Reference {
305 reference:
306 ReferenceType::Range {
307 sheet,
308 start_row,
309 start_col,
310 end_row,
311 end_col,
312 ..
313 },
314 ..
315 } = &node.node_type
316 else {
317 return false;
318 };
319 let sheet_id = match sheet.as_deref() {
320 Some(name) => match graph.sheet_id(name) {
321 Some(id) => id,
322 None => return false,
323 },
324 None => graph.get_vertex_sheet_id(dependent),
325 };
326 sheet_id == range_sheet
327 && start_row.map(|index| index.saturating_sub(1)) == range.0
328 && end_row.map(|index| index.saturating_sub(1)) == range.1
329 && start_col.map(|index| index.saturating_sub(1)) == range.2
330 && end_col.map(|index| index.saturating_sub(1)) == range.3
331 }
332
333 fn selected_region_contains_self(
334 graph: &DependencyGraph,
335 dependent: VertexId,
336 range_sheet: SheetId,
337 range: (Option<u32>, Option<u32>, Option<u32>, Option<u32>),
338 position: i64,
339 explicit_col: Option<i64>,
340 ) -> Option<bool> {
341 let (sr, er, sc, ec) = graph.compressed_range_resolved_bounds(range_sheet, range)?;
342 let (row, col) = match explicit_col {
343 Some(col) => (position, col),
344 None if sr == er => (1, position),
345 None => (position, 1),
346 };
347 if row < 0 || col < 0 {
348 return Some(false);
349 }
350 let coord = graph.store.coord(dependent);
351 let contains = if row == 0 && col == 0 {
352 coord.row() >= sr && coord.row() <= er && coord.col() >= sc && coord.col() <= ec
353 } else if col == 0 {
354 let selected_row = sr.checked_add(u32::try_from(row).ok()?.saturating_sub(1))?;
355 selected_row <= er
356 && coord.row() == selected_row
357 && coord.col() >= sc
358 && coord.col() <= ec
359 } else if row == 0 {
360 let selected_col = sc.checked_add(u32::try_from(col).ok()?.saturating_sub(1))?;
361 selected_col <= ec
362 && coord.col() == selected_col
363 && coord.row() >= sr
364 && coord.row() <= er
365 } else {
366 let selected_row = sr.checked_add(u32::try_from(row).ok()?.saturating_sub(1))?;
367 let selected_col = sc.checked_add(u32::try_from(col).ok()?.saturating_sub(1))?;
368 selected_row <= er
369 && selected_col <= ec
370 && coord.row() == selected_row
371 && coord.col() == selected_col
372 };
373 Some(contains)
374 }
375
376 fn visit(
377 graph: &DependencyGraph,
378 node: &ASTNode,
379 dependent: VertexId,
380 range_sheet: SheetId,
381 range: (Option<u32>, Option<u32>, Option<u32>, Option<u32>),
382 index: Option<(i64, Option<i64>)>,
383 ) -> RangeSelfUse {
384 if matching_range(graph, node, dependent, range_sheet, range) {
385 return match index.and_then(|(row, col)| {
386 selected_region_contains_self(graph, dependent, range_sheet, range, row, col)
387 }) {
388 Some(false) => RangeSelfUse::Excluded,
389 Some(true) | None => RangeSelfUse::IncludedOrUnknown,
390 };
391 }
392 match &node.node_type {
393 ASTNodeType::Function { name, args }
394 if name.eq_ignore_ascii_case("INDEX") && (2..=3).contains(&args.len()) =>
395 {
396 let row = static_index(&args[1]);
397 let col = args.get(2).and_then(static_index);
398 let selection = row.and_then(|row| {
399 if args.len() == 2 || col.is_some() {
400 Some((row, col))
401 } else {
402 None
403 }
404 });
405 let mut use_kind =
406 visit(graph, &args[0], dependent, range_sheet, range, selection);
407 for arg in &args[1..] {
408 use_kind =
409 use_kind.merge(visit(graph, arg, dependent, range_sheet, range, None));
410 }
411 use_kind
412 }
413 ASTNodeType::Function { args, .. } => {
414 args.iter().fold(RangeSelfUse::NoMatch, |kind, arg| {
415 kind.merge(visit(graph, arg, dependent, range_sheet, range, None))
416 })
417 }
418 ASTNodeType::UnaryOp { expr, .. } => {
419 visit(graph, expr, dependent, range_sheet, range, None)
420 }
421 ASTNodeType::BinaryOp { left, right, .. } => visit(
422 graph,
423 left,
424 dependent,
425 range_sheet,
426 range,
427 None,
428 )
429 .merge(visit(graph, right, dependent, range_sheet, range, None)),
430 ASTNodeType::Call { callee, args } => {
431 let mut kind = visit(graph, callee, dependent, range_sheet, range, None);
432 for arg in args {
433 kind = kind.merge(visit(graph, arg, dependent, range_sheet, range, None));
434 }
435 kind
436 }
437 ASTNodeType::Array(rows) => {
438 rows.iter()
439 .flatten()
440 .fold(RangeSelfUse::NoMatch, |kind, item| {
441 kind.merge(visit(graph, item, dependent, range_sheet, range, None))
442 })
443 }
444 ASTNodeType::Literal(_) | ASTNodeType::Omitted | ASTNodeType::Reference { .. } => {
445 RangeSelfUse::NoMatch
446 }
447 }
448 }
449
450 visit(self, &ast, dependent, range_sheet, range, None)
451 }
452
453 pub(super) fn add_range_dependent_edges(
454 &mut self,
455 dependent: VertexId,
456 ranges: &[SharedRangeRef<'static>],
457 current_sheet_id: SheetId,
458 ) {
459 if ranges.is_empty() {
460 return;
461 }
462
463 self.formula_to_range_deps
464 .insert(dependent, ranges.to_vec());
465
466 for range in ranges {
467 let sheet_id = self
471 .sheet_reg
472 .resolve_locator(&range.sheet, current_sheet_id)
473 .unwrap_or(current_sheet_id);
474
475 let s_row = range.start_row.map(|b| b.index);
476 let e_row = range.end_row.map(|b| b.index);
477 let s_col = range.start_col.map(|b| b.index);
478 let e_col = range.end_col.map(|b| b.index);
479
480 if self.range_region_contains_self(dependent, sheet_id, s_row, e_row, s_col, e_col)
485 && self.compressed_range_self_use(dependent, sheet_id, (s_row, e_row, s_col, e_col))
486 != RangeSelfUse::Excluded
487 {
488 self.record_self_loop(dependent);
489 }
490
491 let col_stripes = (s_row.is_none() && e_row.is_none())
492 || (s_col.is_some() && e_col.is_some() && (s_row.is_none() || e_row.is_none()));
493 let row_stripes = (s_col.is_none() && e_col.is_none())
494 || (s_row.is_some() && e_row.is_some() && (s_col.is_none() || e_col.is_none()));
495
496 if col_stripes && !row_stripes {
497 let sc = s_col.unwrap_or(0);
498 let ec = e_col.unwrap_or(sc);
499 for col in sc..=ec {
500 let key = StripeKey {
501 sheet_id,
502 stripe_type: StripeType::Column,
503 index: col,
504 };
505 self.stripe_to_dependents
506 .entry(key.clone())
507 .or_default()
508 .insert(dependent);
509 #[cfg(test)]
510 {
511 if self.stripe_to_dependents.get(&key).map(|s| s.len()) == Some(1)
512 && let Ok(mut g) = self.instr.lock()
513 {
514 g.stripe_inserts += 1;
515 }
516 }
517 }
518 continue;
519 }
520
521 if row_stripes && !col_stripes {
522 let sr = s_row.unwrap_or(0);
523 let er = e_row.unwrap_or(sr);
524 for row in sr..=er {
525 let key = StripeKey {
526 sheet_id,
527 stripe_type: StripeType::Row,
528 index: row,
529 };
530 self.stripe_to_dependents
531 .entry(key.clone())
532 .or_default()
533 .insert(dependent);
534 #[cfg(test)]
535 {
536 if self.stripe_to_dependents.get(&key).map(|s| s.len()) == Some(1)
537 && let Ok(mut g) = self.instr.lock()
538 {
539 g.stripe_inserts += 1;
540 }
541 }
542 }
543 continue;
544 }
545
546 let start_row = s_row.unwrap_or(0);
547 let start_col = s_col.unwrap_or(0);
548 let end_row = e_row.unwrap_or(start_row);
549 let end_col = e_col.unwrap_or(start_col);
550
551 let height = end_row.saturating_sub(start_row) + 1;
552 let width = end_col.saturating_sub(start_col) + 1;
553
554 if self.config.enable_block_stripes && height > 1 && width > 1 {
555 let start_block_row = start_row / BLOCK_H;
556 let end_block_row = end_row / BLOCK_H;
557 let start_block_col = start_col / BLOCK_W;
558 let end_block_col = end_col / BLOCK_W;
559
560 for block_row in start_block_row..=end_block_row {
561 for block_col in start_block_col..=end_block_col {
562 let key = StripeKey {
563 sheet_id,
564 stripe_type: StripeType::Block,
565 index: block_index(block_row * BLOCK_H, block_col * BLOCK_W),
566 };
567 self.stripe_to_dependents
568 .entry(key.clone())
569 .or_default()
570 .insert(dependent);
571 #[cfg(test)]
572 {
573 if self.stripe_to_dependents.get(&key).map(|s| s.len()) == Some(1)
574 && let Ok(mut g) = self.instr.lock()
575 {
576 g.stripe_inserts += 1;
577 }
578 }
579 }
580 }
581 } else if height > width {
582 for col in start_col..=end_col {
583 let key = StripeKey {
584 sheet_id,
585 stripe_type: StripeType::Column,
586 index: col,
587 };
588 self.stripe_to_dependents
589 .entry(key.clone())
590 .or_default()
591 .insert(dependent);
592 #[cfg(test)]
593 {
594 if self.stripe_to_dependents.get(&key).map(|s| s.len()) == Some(1)
595 && let Ok(mut g) = self.instr.lock()
596 {
597 g.stripe_inserts += 1;
598 }
599 }
600 }
601 } else {
602 for row in start_row..=end_row {
603 let key = StripeKey {
604 sheet_id,
605 stripe_type: StripeType::Row,
606 index: row,
607 };
608 self.stripe_to_dependents
609 .entry(key.clone())
610 .or_default()
611 .insert(dependent);
612 #[cfg(test)]
613 {
614 if self.stripe_to_dependents.get(&key).map(|s| s.len()) == Some(1)
615 && let Ok(mut g) = self.instr.lock()
616 {
617 g.stripe_inserts += 1;
618 }
619 }
620 }
621 }
622 }
623 }
624
625 pub fn add_range_deps_from_keys(
627 &mut self,
628 dependent: VertexId,
629 keys: &[crate::engine::plan::RangeKey],
630 current_sheet_id: SheetId,
631 ) {
632 use crate::engine::plan::RangeKey as RK;
633 if keys.is_empty() {
634 return;
635 }
636
637 let mut shared_ranges: Vec<SharedRangeRef<'static>> = Vec::with_capacity(keys.len());
638 for k in keys {
639 let sheet_loc = SharedSheetLocator::Id(match k {
640 RK::Rect { sheet, .. }
641 | RK::WholeRow { sheet, .. }
642 | RK::WholeCol { sheet, .. }
643 | RK::OpenRect { sheet, .. } => *sheet,
644 });
645
646 let mk_axis = |idx0: u32| formualizer_common::AxisBound::new(idx0, false);
647
648 let built = match k {
649 RK::Rect { start, end, .. } => {
650 let sr = mk_axis(start.row());
651 let sc = mk_axis(start.col());
652 let er = mk_axis(end.row());
653 let ec = mk_axis(end.col());
654 SharedRangeRef::from_parts(sheet_loc, Some(sr), Some(sc), Some(er), Some(ec))
655 .ok()
656 }
657 RK::WholeRow { row, .. } => {
658 let r0 = row.saturating_sub(1);
659 let b = mk_axis(r0);
660 SharedRangeRef::from_parts(sheet_loc, Some(b), None, Some(b), None).ok()
661 }
662 RK::WholeCol { col, .. } => {
663 let c0 = col.saturating_sub(1);
664 let b = mk_axis(c0);
665 SharedRangeRef::from_parts(sheet_loc, None, Some(b), None, Some(b)).ok()
666 }
667 RK::OpenRect { start, end, .. } => {
668 let (sr, sc) = match start {
669 Some(p) => (Some(mk_axis(p.row())), Some(mk_axis(p.col()))),
670 None => (None, None),
671 };
672 let (er, ec) = match end {
673 Some(p) => (Some(mk_axis(p.row())), Some(mk_axis(p.col()))),
674 None => (None, None),
675 };
676 SharedRangeRef::from_parts(sheet_loc, sr, sc, er, ec).ok()
677 }
678 };
679
680 if let Some(r) = built {
681 shared_ranges.push(r.into_owned());
682 }
683 }
684
685 if shared_ranges.is_empty() {
686 return;
687 }
688
689 self.formula_to_range_deps
690 .insert(dependent, shared_ranges.clone());
691
692 for range in &shared_ranges {
693 let sheet_id = self
695 .sheet_reg
696 .resolve_locator(&range.sheet, current_sheet_id)
697 .unwrap_or(current_sheet_id);
698
699 let s_row = range.start_row.map(|b| b.index);
700 let e_row = range.end_row.map(|b| b.index);
701 let s_col = range.start_col.map(|b| b.index);
702 let e_col = range.end_col.map(|b| b.index);
703
704 if self.range_region_contains_self(dependent, sheet_id, s_row, e_row, s_col, e_col)
707 && self.compressed_range_self_use(dependent, sheet_id, (s_row, e_row, s_col, e_col))
708 != RangeSelfUse::Excluded
709 {
710 self.record_self_loop(dependent);
711 }
712
713 let col_stripes = (s_row.is_none() && e_row.is_none())
714 || (s_col.is_some() && e_col.is_some() && (s_row.is_none() || e_row.is_none()));
715 let row_stripes = (s_col.is_none() && e_col.is_none())
716 || (s_row.is_some() && e_row.is_some() && (s_col.is_none() || e_col.is_none()));
717
718 if col_stripes && !row_stripes {
719 let sc = s_col.unwrap_or(0);
720 let ec = e_col.unwrap_or(sc);
721 for col in sc..=ec {
722 let key = StripeKey {
723 sheet_id,
724 stripe_type: StripeType::Column,
725 index: col,
726 };
727 self.stripe_to_dependents
728 .entry(key)
729 .or_default()
730 .insert(dependent);
731 }
732 continue;
733 }
734
735 if row_stripes && !col_stripes {
736 let sr = s_row.unwrap_or(0);
737 let er = e_row.unwrap_or(sr);
738 for row in sr..=er {
739 let key = StripeKey {
740 sheet_id,
741 stripe_type: StripeType::Row,
742 index: row,
743 };
744 self.stripe_to_dependents
745 .entry(key)
746 .or_default()
747 .insert(dependent);
748 }
749 continue;
750 }
751
752 let start_row = s_row.unwrap_or(0);
753 let start_col = s_col.unwrap_or(0);
754 let end_row = e_row.unwrap_or(start_row);
755 let end_col = e_col.unwrap_or(start_col);
756
757 let height = end_row.saturating_sub(start_row) + 1;
758 let width = end_col.saturating_sub(start_col) + 1;
759
760 if self.config.enable_block_stripes && height > 1 && width > 1 {
761 let start_block_row = start_row / BLOCK_H;
762 let end_block_row = end_row / BLOCK_H;
763 let start_block_col = start_col / BLOCK_W;
764 let end_block_col = end_col / BLOCK_W;
765
766 for block_row in start_block_row..=end_block_row {
767 for block_col in start_block_col..=end_block_col {
768 let key = StripeKey {
769 sheet_id,
770 stripe_type: StripeType::Block,
771 index: block_index(block_row * BLOCK_H, block_col * BLOCK_W),
772 };
773 self.stripe_to_dependents
774 .entry(key)
775 .or_default()
776 .insert(dependent);
777 }
778 }
779 } else if height > width {
780 for col in start_col..=end_col {
781 let key = StripeKey {
782 sheet_id,
783 stripe_type: StripeType::Column,
784 index: col,
785 };
786 self.stripe_to_dependents
787 .entry(key)
788 .or_default()
789 .insert(dependent);
790 }
791 } else {
792 for row in start_row..=end_row {
793 let key = StripeKey {
794 sheet_id,
795 stripe_type: StripeType::Row,
796 index: row,
797 };
798 self.stripe_to_dependents
799 .entry(key)
800 .or_default()
801 .insert(dependent);
802 }
803 }
804 }
805 }
806}