1use std::collections::btree_map::Range;
21use std::collections::HashMap;
22
23use crate::intrinsics::fold;
24use crate::refers::{self, Kind};
25use crate::sheet::{Cell, Content, Sheet, Workbook};
26
27use super::formula::{ref_expr, RefExpr};
28use super::value::ErrorKind;
29
30pub type CellId = (usize, u32, u32);
32
33pub type Extent = Option<((u32, u32), (u32, u32))>;
35
36#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
40pub struct Area {
41 pub sheet: usize,
42 pub top: u32,
43 pub left: u32,
44 pub bottom: u32,
45 pub right: u32,
46}
47
48impl Area {
49 pub fn is_cell(&self) -> bool {
51 self.top == self.bottom && self.left == self.right
52 }
53}
54
55#[derive(Clone, Debug)]
60pub struct Areas(Held);
61
62#[derive(Clone, Debug)]
63enum Held {
64 One(Area),
65 Span(Vec<Area>),
66}
67
68impl Areas {
69 pub fn one(area: Area) -> Areas {
71 Areas(Held::One(area))
72 }
73}
74
75impl From<Vec<Area>> for Areas {
76 fn from(areas: Vec<Area>) -> Areas {
77 match areas.as_slice() {
78 [area] => Areas::one(*area),
79 _ => Areas(Held::Span(areas)),
80 }
81 }
82}
83
84impl std::ops::Deref for Areas {
85 type Target = [Area];
86
87 fn deref(&self) -> &[Area] {
88 match &self.0 {
89 Held::One(area) => std::slice::from_ref(area),
90 Held::Span(areas) => areas,
91 }
92 }
93}
94
95impl<'a> IntoIterator for &'a Areas {
96 type Item = &'a Area;
97 type IntoIter = std::slice::Iter<'a, Area>;
98
99 fn into_iter(self) -> Self::IntoIter {
100 self.iter()
101 }
102}
103
104impl PartialEq for Areas {
106 fn eq(&self, other: &Areas) -> bool {
107 **self == **other
108 }
109}
110
111impl Eq for Areas {}
112
113pub struct Names {
117 entries: Vec<NameEntry>,
118}
119
120struct NameEntry {
121 key: String,
122 scope: Option<usize>,
123 text: String,
124}
125
126impl Names {
127 pub fn of(model: &Workbook) -> Names {
129 let entries = model
130 .defined_names
131 .iter()
132 .map(|(printed, text)| {
133 let (scope, bare) = split_scope(model, printed);
134 NameEntry {
135 key: fold(bare),
136 scope,
137 text: text.clone(),
138 }
139 })
140 .collect();
141 Names { entries }
142 }
143
144 pub fn resolve(&self, name: &str, scope: Option<usize>, home: usize) -> Option<&str> {
148 let key = fold(name);
149 let scoped = |s: usize| {
150 self.entries
151 .iter()
152 .find(|e| e.key == key && e.scope == Some(s))
153 };
154 let global = || {
155 self.entries
156 .iter()
157 .find(|e| e.key == key && e.scope.is_none())
158 };
159 let entry = match scope {
160 Some(s) => scoped(s).or_else(global),
161 None => scoped(home).or_else(global),
162 };
163 entry.map(|e| e.text.as_str())
164 }
165
166 pub fn len(&self) -> usize {
168 self.entries.len()
169 }
170
171 pub fn is_empty(&self) -> bool {
173 self.entries.is_empty()
174 }
175}
176
177fn split_scope<'a>(model: &Workbook, printed: &'a str) -> (Option<usize>, &'a str) {
180 let Some(bang) = printed.rfind('!') else {
181 return (None, printed);
182 };
183 let (sheet_part, bare) = (&printed[..bang], &printed[bang + 1..]);
184 let sheet =
185 if sheet_part.len() >= 2 && sheet_part.starts_with('\'') && sheet_part.ends_with('\'') {
186 sheet_part[1..sheet_part.len() - 1].replace("''", "'")
187 } else {
188 sheet_part.to_string()
189 };
190 (model.find_sheet(&sheet), bare)
191}
192
193pub fn areas_of(model: &Workbook, home: usize, r: &RefExpr) -> Result<Areas, ErrorKind> {
197 areas_at(model, home, r, 0, 0)
198}
199
200pub fn areas_at(
207 model: &Workbook,
208 home: usize,
209 r: &RefExpr,
210 d_row: i64,
211 d_col: i64,
212) -> Result<Areas, ErrorKind> {
213 let (first, last) = match r.found {
214 Some((first, last)) => (usize::from(first), usize::from(last)),
215 None => {
216 let first = match &r.sheet {
217 None => home,
218 Some(name) => model.find_sheet(name).ok_or(ErrorKind::Ref)?,
219 };
220 let last = match &r.last_sheet {
221 None => first,
222 Some(name) => model.find_sheet(name).ok_or(ErrorKind::Ref)?,
223 };
224 (first, last)
225 }
226 };
227 let (top, left, bottom, right) = r.moved(d_row, d_col).ok_or(ErrorKind::Ref)?;
228 let area = |sheet| Area {
229 sheet,
230 top,
231 left,
232 bottom,
233 right,
234 };
235 if first == last {
236 return Ok(Areas::one(area(first)));
237 }
238 let (a, b) = (first.min(last), first.max(last));
239 Ok(Areas::from((a..=b).map(area).collect::<Vec<Area>>()))
240}
241
242pub fn cells_in<'a>(
248 sheet: &'a Sheet,
249 extent: Extent,
250 area: &Area,
251) -> impl Iterator<Item = ((u32, u32), &'a Cell)> + 'a {
252 let Some(((e_top, e_left), (e_bottom, e_right))) = extent else {
253 return Walk::Done;
254 };
255 let top = area.top.max(e_top);
256 let bottom = area.bottom.min(e_bottom);
257 let left = area.left.max(e_left);
258 let right = area.right.min(e_right);
259 if top > bottom || left > right {
260 return Walk::Done;
261 }
262 let rows = usize::try_from(bottom - top + 1).unwrap_or(usize::MAX);
263 if rows > sheet.cells.len() {
264 return Walk::Sparse {
266 cells: sheet.cells.range((top, 0)..=(bottom, u32::MAX)),
267 left,
268 right,
269 };
270 }
271 Walk::Rows {
272 sheet,
273 row: top,
274 bottom,
275 left,
276 right,
277 cells: sheet.cells.range((top, left)..=(top, right)),
278 }
279}
280
281enum Walk<'a> {
285 Done,
286 Sparse {
287 cells: Range<'a, (u32, u32), Cell>,
288 left: u32,
289 right: u32,
290 },
291 Rows {
292 sheet: &'a Sheet,
293 row: u32,
294 bottom: u32,
295 left: u32,
296 right: u32,
297 cells: Range<'a, (u32, u32), Cell>,
298 },
299}
300
301impl<'a> Iterator for Walk<'a> {
302 type Item = ((u32, u32), &'a Cell);
303
304 fn next(&mut self) -> Option<Self::Item> {
305 match self {
306 Walk::Done => None,
307 Walk::Sparse { cells, left, right } => {
308 let (left, right) = (*left, *right);
309 cells
310 .find(|((_, col), _)| *col >= left && *col <= right)
311 .map(|(k, c)| (*k, c))
312 }
313 Walk::Rows {
314 sheet,
315 row,
316 bottom,
317 left,
318 right,
319 cells,
320 } => loop {
321 if let Some((k, c)) = cells.next() {
322 return Some((*k, c));
323 }
324 if *row >= *bottom {
325 return None;
326 }
327 *row += 1;
328 *cells = sheet.cells.range((*row, *left)..=(*row, *right));
329 },
330 }
331 }
332}
333
334pub fn is_formula(content: &Content) -> bool {
336 matches!(
337 content,
338 Content::Formula(_)
339 | Content::SharedMaster { .. }
340 | Content::SharedChild { .. }
341 | Content::DynamicFormula(_)
342 )
343}
344
345const NAME_DEPTH: u32 = 8;
347
348pub fn dependencies(
351 model: &Workbook,
352 names: &Names,
353 extents: &[Extent],
354 home: usize,
355 text: &str,
356 out: &mut Vec<CellId>,
357) {
358 collect(model, names, extents, home, text, 0, out);
359}
360
361fn collect(
362 model: &Workbook,
363 names: &Names,
364 extents: &[Extent],
365 home: usize,
366 text: &str,
367 depth: u32,
368 out: &mut Vec<CellId>,
369) {
370 for r in refers::scan(text) {
371 match r.kind {
372 Kind::Cell | Kind::Range | Kind::Column | Kind::Row | Kind::ThreeD => {
373 let Some(re) = ref_expr(&r) else { continue };
374 let Ok(areas) = areas_of(model, home, &re) else {
375 continue;
376 };
377 for a in &areas {
378 let sheet = &model.sheets[a.sheet];
379 for ((row, col), cell) in cells_in(sheet, extents[a.sheet], a) {
380 if is_formula(&cell.content) {
381 out.push((a.sheet, row, col));
382 }
383 }
384 }
385 }
386 Kind::Name if depth < NAME_DEPTH => {
387 let scope = if r.sheet_name.is_empty() {
388 None
389 } else {
390 model.find_sheet(&r.sheet_name)
391 };
392 if let Some(t) = names.resolve(&r.part, scope, home) {
393 collect(
394 model,
395 names,
396 extents,
397 scope.unwrap_or(home),
398 t,
399 depth + 1,
400 out,
401 );
402 }
403 }
404 _ => {}
405 }
406 }
407}
408
409#[derive(Clone, Debug, Default, PartialEq, Eq)]
412pub struct Plan {
413 pub order: Vec<CellId>,
415 pub cycles: Vec<Vec<CellId>>,
417}
418
419impl Plan {
420 pub fn formulas(&self) -> usize {
422 self.order.len() + self.cycles.iter().map(Vec::len).sum::<usize>()
423 }
424}
425
426pub fn plan(
429 model: &Workbook,
430 names: &Names,
431 extents: &[Extent],
432 texts: &HashMap<CellId, String>,
433) -> Plan {
434 let mut ids: Vec<CellId> = texts.keys().copied().collect();
435 ids.sort_unstable();
436 let index: HashMap<CellId, usize> = ids.iter().enumerate().map(|(i, id)| (*id, i)).collect();
437 let n = ids.len();
438 let mut adj: Vec<Vec<usize>> = vec![Vec::new(); n];
439 let mut deps = Vec::new();
440 for (vi, id) in ids.iter().enumerate() {
441 deps.clear();
442 dependencies(model, names, extents, id.0, &texts[id], &mut deps);
443 let edges = &mut adj[vi];
444 for d in &deps {
445 if let Some(&di) = index.get(d) {
446 edges.push(di);
447 }
448 }
449 edges.sort_unstable();
450 edges.dedup();
451 }
452 let components = tarjan(&adj);
453 let mut plan = Plan::default();
454 for comp in components {
455 if comp.len() == 1 && !adj[comp[0]].contains(&comp[0]) {
456 plan.order.push(ids[comp[0]]);
457 } else {
458 let mut cells: Vec<CellId> = comp.iter().map(|&i| ids[i]).collect();
459 cells.sort_unstable();
460 plan.cycles.push(cells);
461 }
462 }
463 plan.cycles.sort();
464 plan
465}
466
467pub(crate) fn tarjan(adj: &[Vec<usize>]) -> Vec<Vec<usize>> {
473 let mut offsets = Vec::with_capacity(adj.len() + 1);
474 let mut targets = Vec::new();
475 offsets.push(0);
476 for edges in adj {
477 targets.extend(edges.iter().map(|&w| w as u32));
478 offsets.push(targets.len());
479 }
480 let (members, starts) = tarjan_flat(&offsets, &targets);
481 starts
482 .windows(2)
483 .map(|c| members[c[0]..c[1]].iter().map(|&v| v as usize).collect())
484 .collect()
485}
486
487pub(crate) fn tarjan_flat(offsets: &[usize], targets: &[u32]) -> (Vec<u32>, Vec<usize>) {
494 const NONE: u32 = u32::MAX;
495 let n = offsets.len().saturating_sub(1);
496 let mut index = vec![NONE; n];
497 let mut low = vec![0u32; n];
498 let mut on_stack = vec![false; n];
499 let mut stack: Vec<u32> = Vec::new();
500 let mut members: Vec<u32> = Vec::with_capacity(n);
501 let mut starts: Vec<usize> = Vec::with_capacity(n + 1);
502 starts.push(0);
503 let mut counter = 0u32;
504 let mut calls: Vec<(usize, usize)> = Vec::new();
506 for root in 0..n {
507 if index[root] != NONE {
508 continue;
509 }
510 calls.push((root, offsets[root]));
511 index[root] = counter;
512 low[root] = counter;
513 counter += 1;
514 stack.push(root as u32);
515 on_stack[root] = true;
516 while let Some(&mut (v, ref mut next)) = calls.last_mut() {
517 if *next < offsets[v + 1] {
518 let w = targets[*next] as usize;
519 *next += 1;
520 if index[w] == NONE {
521 index[w] = counter;
522 low[w] = counter;
523 counter += 1;
524 stack.push(w as u32);
525 on_stack[w] = true;
526 calls.push((w, offsets[w]));
527 } else if on_stack[w] {
528 low[v] = low[v].min(index[w]);
529 }
530 continue;
531 }
532 calls.pop();
533 if let Some(&(u, _)) = calls.last() {
534 low[u] = low[u].min(low[v]);
535 }
536 if low[v] == index[v] {
537 let from = members.len();
538 while let Some(w) = stack.pop() {
539 on_stack[w as usize] = false;
540 members.push(w);
541 if w as usize == v {
542 break;
543 }
544 }
545 members[from..].sort_unstable();
546 starts.push(members.len());
547 }
548 }
549 }
550 (members, starts)
551}
552
553#[cfg(test)]
554mod tests {
555 use super::*;
556
557 #[test]
558 fn tarjan_emits_dependencies_before_their_readers() {
559 let adj = vec![vec![1, 2], vec![2], vec![], vec![4], vec![3], vec![5]];
561 let comps = tarjan(&adj);
562 let pos = |x: usize| comps.iter().position(|c| c.contains(&x)).unwrap();
563 assert!(pos(2) < pos(1) && pos(1) < pos(0));
564 assert!(comps.contains(&vec![3, 4]));
565 assert!(comps.contains(&vec![5]));
566 assert_eq!(comps.len(), 5);
567 }
568
569 #[test]
570 fn the_flat_walk_finds_every_component_after_all_it_reads() {
571 let mut seed: u64 = 0x5EED_0033;
577 let mut below = |n: usize| {
578 seed = seed
579 .wrapping_mul(6_364_136_223_846_793_005)
580 .wrapping_add(1_442_695_040_888_963_407);
581 ((seed >> 33) % n as u64) as usize
582 };
583 for case in 0..400 {
584 let n = 1 + below(14);
585 let density = 1 + below(5);
586 let adj: Vec<Vec<usize>> = (0..n)
587 .map(|_| {
588 let mut edges: Vec<usize> = (0..below(density * 2)).map(|_| below(n)).collect();
589 edges.sort_unstable();
590 edges.dedup();
591 edges
592 })
593 .collect();
594 let mut reach = vec![vec![false; n]; n];
595 for (v, row) in reach.iter_mut().enumerate() {
596 let mut stack = vec![v];
597 while let Some(u) = stack.pop() {
598 for &w in &adj[u] {
599 if !row[w] {
600 row[w] = true;
601 stack.push(w);
602 }
603 }
604 }
605 }
606 let mut want: Vec<Vec<usize>> = Vec::new();
607 for (v, from_v) in reach.iter().enumerate() {
608 if want.iter().any(|c| c.contains(&v)) {
609 continue;
610 }
611 want.push(
612 (0..n)
613 .filter(|&u| u == v || (from_v[u] && reach[u][v]))
614 .collect(),
615 );
616 }
617 let comps = tarjan(&adj);
618 let mut got = comps.clone();
619 got.sort();
620 want.sort();
621 assert_eq!(got, want, "case {case}: {adj:?}");
622 let at = |v: usize| comps.iter().position(|c| c.contains(&v)).unwrap();
623 for (v, edges) in adj.iter().enumerate() {
624 for &w in edges {
625 assert!(at(w) <= at(v), "case {case}: {v} reads {w}: {adj:?}");
626 }
627 }
628 }
629 }
630
631 #[test]
632 fn a_reference_s_areas_are_one_held_in_place_or_a_span_s_in_order() {
633 use super::super::formula::{parse, Expr};
636 let mut wb = Workbook::new();
637 for name in ["Jan", "Feb", "Mar"] {
638 wb.sheets.push(Sheet::new(name));
639 }
640 let areas = |text: &str| {
641 let Ok(Expr::Ref(r)) = parse(text) else {
642 panic!("{text}")
643 };
644 areas_of(&wb, 0, &r)
645 };
646 let rect = |sheet, top, left, bottom, right| Area {
647 sheet,
648 top,
649 left,
650 bottom,
651 right,
652 };
653 let one = areas("=Feb!B2:C3").unwrap();
654 assert_eq!(&one[..], &[rect(1, 2, 2, 3, 3)]);
655 assert_eq!(one, Areas::one(rect(1, 2, 2, 3, 3)));
656 assert_eq!(one, Areas::from(vec![rect(1, 2, 2, 3, 3)]));
657 assert_eq!(
658 &areas("=B2").unwrap()[..],
659 &[rect(0, 2, 2, 2, 2)],
660 "the formula's own sheet"
661 );
662 let span = areas("=Jan:Mar!B2").unwrap();
663 let sheets: Vec<usize> = span.iter().map(|a| a.sheet).collect();
664 assert_eq!(sheets, vec![0, 1, 2]);
665 assert_eq!(areas("=Mar:Jan!B2").unwrap(), span);
666 assert_ne!(span, one);
667 assert_eq!(areas("=Apr!B2"), Err(ErrorKind::Ref));
668 }
669
670 #[test]
671 fn a_range_s_cells_are_walked_row_by_row_as_they_were_collected() {
672 use crate::sheet::{MAX_COLUMN, MAX_ROW};
678 let number = |n: u32| Cell {
679 content: Content::Number(f64::from(n)),
680 style: 0,
681 };
682 let mut dense = Sheet::new("Dense");
683 for row in 1..=5u32 {
684 for col in 1..=4u32 {
685 if (row + col) % 5 != 0 {
686 dense.set(row, col, number(row * 10 + col));
687 }
688 }
689 }
690 let mut sparse = Sheet::new("Sparse");
691 for (row, col) in [(1, 2), (40, 1), (40, 3), (700, 2), (1000, 4)] {
692 sparse.set(row, col, number(row));
693 }
694 let empty = Sheet::new("Empty");
695 let rect = |top, left, bottom, right| Area {
696 sheet: 0,
697 top,
698 left,
699 bottom,
700 right,
701 };
702 let areas = [
703 rect(2, 2, 4, 3),
704 rect(1, 1, MAX_ROW, 1),
705 rect(3, 1, 3, MAX_COLUMN),
706 rect(1, 1, MAX_ROW, MAX_COLUMN),
707 rect(40, 2, 700, 3),
708 rect(6, 1, 9, 9),
709 rect(2, 5, 4, 9),
710 rect(1, 2, 1, 2),
711 ];
712 let mut walked_any = 0;
713 for sheet in [&dense, &sparse, &empty] {
714 let extent = sheet.extent();
715 for a in &areas {
716 let walked: Vec<(u32, u32)> = cells_in(sheet, extent, a).map(|(k, _)| k).collect();
717 let want: Vec<(u32, u32)> = match extent {
718 None => Vec::new(),
719 Some(((t, l), (b, r))) => sheet
720 .cells
721 .keys()
722 .copied()
723 .filter(|&(row, col)| {
724 row >= a.top.max(t)
725 && row <= a.bottom.min(b)
726 && col >= a.left.max(l)
727 && col <= a.right.min(r)
728 })
729 .collect(),
730 };
731 assert_eq!(walked, want, "{} {a:?}", sheet.name);
732 walked_any += walked.len();
733 }
734 }
735 assert_eq!(
736 walked_any, 37,
737 "28 cells of the dense sheet's, 9 of the sparse one's"
738 );
739 }
740
741 #[test]
742 fn a_deep_chain_does_not_overflow() {
743 let n = 200_000;
744 let adj: Vec<Vec<usize>> = (0..n)
745 .map(|i| if i + 1 < n { vec![i + 1] } else { vec![] })
746 .collect();
747 let comps = tarjan(&adj);
748 assert_eq!(comps.len(), n);
749 assert_eq!(comps[0], vec![n - 1]);
750 assert_eq!(comps[n - 1], vec![0]);
751 }
752}