1use std::collections::{HashMap, HashSet, VecDeque};
2
3use crate::formula::ast::*;
4use crate::formula::eval;
5use crate::formula::parser;
6use crate::model::{CellError, CellPos, CellValue, Sheet};
7
8pub struct DepGraph {
9 pub dependents: HashMap<CellPos, HashSet<CellPos>>,
10 pub dependencies: HashMap<CellPos, HashSet<CellPos>>,
11}
12
13impl Default for DepGraph {
14 fn default() -> Self {
15 Self::new()
16 }
17}
18
19impl DepGraph {
20 pub fn new() -> Self {
21 DepGraph {
22 dependents: HashMap::new(),
23 dependencies: HashMap::new(),
24 }
25 }
26
27 pub fn set_dependencies(&mut self, cell: CellPos, deps: Vec<CellPos>) {
28 if let Some(old_deps) = self.dependencies.remove(&cell) {
29 for dep in &old_deps {
30 if let Some(set) = self.dependents.get_mut(dep) {
31 set.remove(&cell);
32 }
33 }
34 }
35 let dep_set: HashSet<CellPos> = deps.into_iter().collect();
36 for dep in &dep_set {
37 self.dependents.entry(*dep).or_default().insert(cell);
38 }
39 self.dependencies.insert(cell, dep_set);
40 }
41
42 pub fn remove(&mut self, cell: CellPos) {
46 if let Some(old_deps) = self.dependencies.remove(&cell) {
47 for dep in &old_deps {
48 if let Some(set) = self.dependents.get_mut(dep) {
49 set.remove(&cell);
50 }
51 }
52 }
53 }
54}
55
56pub fn extract_deps(formula: &str) -> Vec<CellPos> {
57 let expr = match parser::parse(formula) {
58 Ok(e) => e,
59 Err(_) => return vec![],
60 };
61 let mut deps = Vec::new();
62 collect_refs(&expr, &mut deps);
63 deps
64}
65
66fn collect_refs(expr: &Expr, out: &mut Vec<CellPos>) {
67 match expr {
68 Expr::CellRef(r) => {
69 out.push((r.row, r.col));
70 }
71 Expr::Range { start, end } => {
72 let r1 = start.row.min(end.row);
73 let r2 = start.row.max(end.row);
74 let c1 = start.col.min(end.col);
75 let c2 = start.col.max(end.col);
76 for r in r1..=r2 {
77 for c in c1..=c2 {
78 out.push((r, c));
79 }
80 }
81 }
82 Expr::BinaryOp { left, right, .. } => {
83 collect_refs(left, out);
84 collect_refs(right, out);
85 }
86 Expr::UnaryNeg(inner) => collect_refs(inner, out),
87 Expr::FnCall { args, .. } => {
88 for arg in args {
89 collect_refs(arg, out);
90 }
91 }
92 _ => {}
93 }
94}
95
96pub fn set_formula(sheet: &mut Sheet, deps: &mut DepGraph, pos: CellPos, raw: &str) {
97 sheet.set_cell(pos, raw);
98 let formula = &raw[1..]; let dep_list = extract_deps(formula);
100 deps.set_dependencies(pos, dep_list);
101}
102
103pub fn mark_dirty(sheet: &mut Sheet, deps: &DepGraph, pos: CellPos) {
104 let mut queue = VecDeque::new();
105 queue.push_back(pos);
106 while let Some(cell) = queue.pop_front() {
107 if let Some(dependents) = deps.dependents.get(&cell) {
108 for &dep in dependents {
109 if let Some(c) = sheet.cells.get_mut(&dep) {
110 if !c.dirty {
111 c.dirty = true;
112 queue.push_back(dep);
113 }
114 }
115 }
116 }
117 }
118}
119
120pub fn recalculate(sheet: &mut Sheet, deps: &DepGraph) {
121 let formula_cells: Vec<CellPos> = sheet
122 .cells
123 .iter()
124 .filter(|(_, cell)| cell.raw.starts_with('='))
125 .map(|(pos, _)| *pos)
126 .collect();
127
128 let formula_set: HashSet<CellPos> = formula_cells.iter().cloned().collect();
129
130 let mut in_degree: HashMap<CellPos, usize> = HashMap::new();
131 for &cell in &formula_cells {
132 let count = deps
133 .dependencies
134 .get(&cell)
135 .map(|d| d.iter().filter(|p| formula_set.contains(p)).count())
136 .unwrap_or(0);
137 in_degree.insert(cell, count);
138 }
139
140 let mut queue: VecDeque<CellPos> = in_degree
141 .iter()
142 .filter(|(_, °)| deg == 0)
143 .map(|(&pos, _)| pos)
144 .collect();
145
146 let mut order = Vec::new();
147
148 while let Some(cell) = queue.pop_front() {
149 order.push(cell);
150 if let Some(dependents) = deps.dependents.get(&cell) {
151 for &dep in dependents {
152 if let Some(deg) = in_degree.get_mut(&dep) {
153 *deg -= 1;
154 if *deg == 0 {
155 queue.push_back(dep);
156 }
157 }
158 }
159 }
160 }
161
162 let ordered_set: HashSet<CellPos> = order.iter().cloned().collect();
163 for &cell in &formula_cells {
164 if !ordered_set.contains(&cell) {
165 if let Some(c) = sheet.cells.get_mut(&cell) {
166 c.value = CellValue::Error(CellError::Circ);
167 c.dirty = false;
168 }
169 }
170 }
171
172 for pos in order {
173 let raw = match sheet.get_cell(pos) {
174 Some(cell) if cell.raw.starts_with('=') => cell.raw.clone(),
175 _ => continue,
176 };
177 let formula = &raw[1..];
178 let value = eval::evaluate(formula, sheet);
179 if let Some(cell) = sheet.cells.get_mut(&pos) {
180 cell.value = value;
181 cell.dirty = false;
182 }
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189 use crate::model::{CellValue, Sheet};
190
191 #[test]
192 fn extract_deps_cell_ref() {
193 let deps = extract_deps("A1+B1");
194 assert_eq!(deps, vec![(0, 0), (0, 1)]);
195 }
196
197 #[test]
198 fn extract_deps_range() {
199 let deps = extract_deps("SUM(A1:A3)");
200 assert_eq!(deps, vec![(0, 0), (1, 0), (2, 0)]);
201 }
202
203 #[test]
204 fn extract_deps_no_refs() {
205 let deps = extract_deps("1+2");
206 assert!(deps.is_empty());
207 }
208
209 #[test]
210 fn recalc_simple() {
211 let mut sheet = Sheet::new();
212 let mut deps = DepGraph::new();
213 sheet.set_cell((0, 0), "10");
214 set_formula(&mut sheet, &mut deps, (0, 1), "=A1+5");
215 recalculate(&mut sheet, &deps);
216 assert_eq!(
217 sheet.get_cell((0, 1)).unwrap().value,
218 CellValue::Number(15.0)
219 );
220 }
221
222 #[test]
223 fn recalc_chain() {
224 let mut sheet = Sheet::new();
225 let mut deps = DepGraph::new();
226 sheet.set_cell((0, 0), "10");
227 set_formula(&mut sheet, &mut deps, (0, 1), "=A1*2");
228 set_formula(&mut sheet, &mut deps, (0, 2), "=B1+1");
229 recalculate(&mut sheet, &deps);
230 assert_eq!(
231 sheet.get_cell((0, 1)).unwrap().value,
232 CellValue::Number(20.0)
233 );
234 assert_eq!(
235 sheet.get_cell((0, 2)).unwrap().value,
236 CellValue::Number(21.0)
237 );
238 }
239
240 #[test]
241 fn recalc_circular_reference() {
242 let mut sheet = Sheet::new();
243 let mut deps = DepGraph::new();
244 set_formula(&mut sheet, &mut deps, (0, 0), "=B1");
245 set_formula(&mut sheet, &mut deps, (0, 1), "=A1");
246 recalculate(&mut sheet, &deps);
247 assert_eq!(
248 sheet.get_cell((0, 0)).unwrap().value,
249 CellValue::Error(CellError::Circ)
250 );
251 assert_eq!(
252 sheet.get_cell((0, 1)).unwrap().value,
253 CellValue::Error(CellError::Circ)
254 );
255 }
256
257 #[test]
258 fn recalc_after_value_change() {
259 let mut sheet = Sheet::new();
260 let mut deps = DepGraph::new();
261 sheet.set_cell((0, 0), "10");
262 set_formula(&mut sheet, &mut deps, (0, 1), "=A1+5");
263 recalculate(&mut sheet, &deps);
264 assert_eq!(
265 sheet.get_cell((0, 1)).unwrap().value,
266 CellValue::Number(15.0)
267 );
268
269 sheet.set_cell((0, 0), "20");
270 mark_dirty(&mut sheet, &deps, (0, 0));
271 recalculate(&mut sheet, &deps);
272 assert_eq!(
273 sheet.get_cell((0, 1)).unwrap().value,
274 CellValue::Number(25.0)
275 );
276 }
277
278 #[test]
279 fn remove_drops_edges() {
280 let mut sheet = Sheet::new();
281 let mut deps = DepGraph::new();
282 sheet.set_cell((0, 0), "10");
283 set_formula(&mut sheet, &mut deps, (0, 1), "=A1+5");
284 assert!(deps.dependencies.contains_key(&(0, 1)));
285 assert!(deps.dependents.get(&(0, 0)).unwrap().contains(&(0, 1)));
286
287 deps.remove((0, 1));
288
289 assert!(!deps.dependencies.contains_key(&(0, 1)));
290 assert!(!deps
291 .dependents
292 .get(&(0, 0))
293 .map(|s| s.contains(&(0, 1)))
294 .unwrap_or(false));
295 }
296
297 #[test]
298 fn self_reference_is_circular() {
299 let mut sheet = Sheet::new();
300 let mut deps = DepGraph::new();
301 set_formula(&mut sheet, &mut deps, (0, 0), "=A1+1");
302 recalculate(&mut sheet, &deps);
303 assert_eq!(
304 sheet.get_cell((0, 0)).unwrap().value,
305 CellValue::Error(CellError::Circ)
306 );
307 }
308}