1use super::graph::{Area, Extent};
41use super::value::Value;
42
43pub(crate) const SLOTS_A_CELL: u64 = 4;
49
50pub(crate) fn limit(cells: usize, most: u64) -> u64 {
55 (cells as u64).saturating_mul(SLOTS_A_CELL).min(most)
56}
57
58pub(crate) fn fits(extent: Extent, limit: u64) -> bool {
61 extent.is_some_and(|((top, left), (bottom, right))| {
62 u64::from(right - left + 1) * u64::from(bottom - top + 1) <= limit
63 })
64}
65
66const NUMBER: u8 = 0;
70const OTHER: u8 = 1;
71
72#[inline]
74fn number_of(v: &Value) -> (f64, u8) {
75 match v {
76 Value::Number(n) => (*n, NUMBER),
77 _ => (0.0, OTHER),
78 }
79}
80
81#[derive(Clone, Debug, PartialEq)]
83pub struct Dense {
84 top: u32,
85 left: u32,
86 width: usize,
87 height: usize,
88 values: Vec<Value>,
89 shadow: Option<Shadow>,
92}
93
94#[derive(Clone, Debug, Default, PartialEq)]
97struct Shadow {
98 numbers: Vec<f64>,
99 kinds: Vec<u8>,
100 odd: Vec<u32>,
101}
102
103impl Dense {
104 pub(crate) fn over(
112 extent: Extent,
113 limit: u64,
114 old: Option<Dense>,
115 numbers: bool,
116 ) -> Option<Dense> {
117 let ((top, left), (bottom, right)) = extent?;
118 if !fits(extent, limit) {
119 return None;
120 }
121 let width = (right - left + 1) as usize;
122 let height = (bottom - top + 1) as usize;
123 let (mut values, spare) = match old {
124 Some(mut d) if d.stands_over(extent) => {
125 d.shade(numbers);
126 return Some(d);
127 }
128 Some(d) => (d.values, d.shadow),
129 None => (Vec::new(), None),
130 };
131 values.clear();
132 values.resize(width * height, Value::Empty);
133 let mut d = Dense {
134 top,
135 left,
136 width,
137 height,
138 values,
139 shadow: None,
140 };
141 if numbers {
142 d.shadow = Some(d.made(spare.unwrap_or_default()));
143 }
144 Some(d)
145 }
146
147 pub(crate) fn shade(&mut self, numbers: bool) {
150 match (numbers, self.shadow.is_some()) {
151 (true, false) => self.shadow = Some(self.made(Shadow::default())),
152 (false, true) => self.shadow = None,
153 _ => {}
154 }
155 }
156
157 #[cfg(test)]
159 pub(crate) fn keeps_numbers(&self) -> bool {
160 self.shadow.is_some()
161 }
162
163 fn made(&self, mut s: Shadow) -> Shadow {
166 let n = self.values.len();
167 s.numbers.clear();
168 s.numbers.reserve(n);
169 s.kinds.clear();
170 s.kinds.reserve(n);
171 for v in &self.values {
172 let (x, kind) = number_of(v);
173 s.numbers.push(x);
174 s.kinds.push(kind);
175 }
176 s.odd.clear();
177 s.odd.extend(
178 s.kinds
179 .chunks(self.width)
180 .map(|row| row.iter().filter(|&&k| k != NUMBER).count() as u32),
181 );
182 s
183 }
184
185 pub(crate) fn stands_over(&self, extent: Extent) -> bool {
189 extent.is_some_and(|((top, left), (bottom, right))| {
190 let width = (right - left + 1) as usize;
191 let height = (bottom - top + 1) as usize;
192 (self.top, self.left, self.width, self.height) == (top, left, width, height)
193 })
194 }
195
196 #[cfg(test)]
201 pub(crate) fn set(&mut self, row: u32, col: u32, v: Value) -> bool {
202 let Some(i) = self.index(row, col) else {
203 return false;
204 };
205 self.write_at(i, |slot| *slot = v);
206 true
207 }
208
209 pub fn get(&self, row: u32, col: u32) -> Option<&Value> {
212 self.index(row, col).map(|i| &self.values[i])
213 }
214
215 pub(crate) fn index(&self, row: u32, col: u32) -> Option<usize> {
217 let r = row.checked_sub(self.top)? as usize;
218 let c = col.checked_sub(self.left)? as usize;
219 (r < self.height && c < self.width).then(|| r * self.width + c)
220 }
221
222 pub(crate) fn slots(&self) -> usize {
224 self.values.len()
225 }
226
227 pub(crate) fn slot_at(&self, i: usize) -> &Value {
230 &self.values[i]
231 }
232
233 #[inline]
238 pub(crate) fn write_at(&mut self, i: usize, f: impl FnOnce(&mut Value)) {
239 f(&mut self.values[i]);
240 let Some(s) = self.shadow.as_mut() else {
241 return;
242 };
243 let (n, kind) = number_of(&self.values[i]);
244 if s.kinds[i] != kind {
245 let row = i / self.width;
246 if kind == NUMBER {
247 s.odd[row] -= 1;
248 } else {
249 s.odd[row] += 1;
250 }
251 s.kinds[i] = kind;
252 }
253 s.numbers[i] = n;
254 }
255
256 pub(crate) fn inside(
263 &self,
264 (top, bottom): (u32, u32),
265 col: u32,
266 width: usize,
267 n: usize,
268 moves: bool,
269 ) -> (usize, usize) {
270 let (first, last) = (self.top, self.top + (self.height as u32 - 1));
271 if top < first || bottom > last {
272 return (0, 0);
273 }
274 let (left, right) = (
275 i64::from(self.left),
276 i64::from(self.left) + self.width as i64 - 1,
277 );
278 let (col, width) = (i64::from(col), width as i64);
279 if !moves {
280 let fits = col >= left && col + width - 1 <= right;
281 return if fits { (0, n) } else { (0, 0) };
282 }
283 let a = (left - col).clamp(0, n as i64);
284 let b = (right - (col + width - 1) + 1).clamp(0, n as i64);
285 if a >= b {
286 (0, 0)
287 } else {
288 (a as usize, b as usize)
289 }
290 }
291
292 #[inline]
298 pub(crate) fn numbers_in_row(&self, row: u32, col: u32, n: usize) -> Option<&[f64]> {
299 let s = self.shadow.as_ref()?;
300 let r = row.checked_sub(self.top)? as usize;
301 let c = col.checked_sub(self.left)? as usize;
302 if r >= self.height || c + n > self.width {
303 return None;
304 }
305 let i = r * self.width + c;
306 if s.odd[r] != 0 && s.kinds[i..i + n].iter().any(|&k| k != NUMBER) {
307 return None;
308 }
309 Some(&s.numbers[i..i + n])
310 }
311
312 #[inline]
319 pub(crate) fn numbers_in_column(
320 &self,
321 row: u32,
322 col: u32,
323 n: usize,
324 out: &mut Vec<f64>,
325 ) -> bool {
326 let Some(s) = self.shadow.as_ref() else {
327 return false;
328 };
329 let (Some(r), Some(c)) = (row.checked_sub(self.top), col.checked_sub(self.left)) else {
330 return false;
331 };
332 let (r, c, w) = (r as usize, c as usize, self.width);
333 if r + n > self.height || c >= w {
334 return false;
335 }
336 let first = r * w + c;
337 if s.odd[r..r + n].iter().any(|&o| o != 0)
338 && (0..n).any(|j| s.kinds[first + j * w] != NUMBER)
339 {
340 return false;
341 }
342 out.extend((0..n).map(|j| s.numbers[first + j * w]));
343 true
344 }
345
346 pub(crate) fn at(&self, i: usize) -> (&Value, (u32, u32)) {
348 let (r, c) = (i / self.width, i % self.width);
349 (&self.values[i], (self.top + r as u32, self.left + c as u32))
350 }
351
352 pub fn each<E, F>(&self, area: &Area, mut f: F) -> Result<(), E>
357 where
358 F: FnMut(Value) -> Result<(), E>,
359 {
360 self.each_ref(area, |v| f(v.clone()))
361 }
362
363 pub(crate) fn each_ref<E, F>(&self, area: &Area, mut f: F) -> Result<(), E>
367 where
368 F: FnMut(&Value) -> Result<(), E>,
369 {
370 let bottom = self.top + (self.height as u32 - 1);
371 let right = self.left + (self.width as u32 - 1);
372 let (top, left) = (area.top.max(self.top), area.left.max(self.left));
373 let (last_row, last_col) = (area.bottom.min(bottom), area.right.min(right));
374 if top > last_row || left > last_col {
375 return Ok(());
376 }
377 let n = (last_col - left + 1) as usize;
378 for row in top..=last_row {
379 let from = (row - self.top) as usize * self.width + (left - self.left) as usize;
380 for v in &self.values[from..from + n] {
381 if !matches!(v, Value::Empty) {
382 f(v)?;
383 }
384 }
385 }
386 Ok(())
387 }
388}
389
390#[cfg(test)]
391mod tests {
392 use super::*;
393 use crate::calc::ErrorKind;
394 use crate::sheet::{MAX_COLUMN, MAX_ROW};
395
396 fn rect(top: u32, left: u32, bottom: u32, right: u32) -> Area {
397 Area {
398 sheet: 0,
399 top,
400 left,
401 bottom,
402 right,
403 }
404 }
405
406 fn walk(d: &Dense, area: Area) -> Vec<Value> {
407 let mut out = Vec::new();
408 d.each::<(), _>(&area, |v| {
409 out.push(v);
410 Ok(())
411 })
412 .unwrap();
413 out
414 }
415
416 #[test]
417 fn a_store_is_its_extent_row_by_row_and_skips_what_is_empty() {
418 let mut d = Dense::over(Some(((2, 2), (4, 4))), 9, None, true).unwrap();
420 let n = Value::Number;
421 let kinds = [
422 n(1.0),
423 Value::Text("x".to_string()),
424 Value::Bool(true),
425 n(4.0),
426 Value::Error(ErrorKind::NA),
427 n(-0.5),
428 n(7.0),
429 n(8.0),
430 ];
431 let cells = [
432 (2, 2),
433 (2, 3),
434 (2, 4),
435 (3, 2),
436 (3, 4),
437 (4, 2),
438 (4, 3),
439 (4, 4),
440 ];
441 for ((row, col), v) in cells.iter().zip(&kinds) {
442 assert!(d.set(*row, *col, v.clone()));
443 }
444 assert!(!d.set(1, 2, Value::Empty), "above the extent");
445 assert!(!d.set(2, 5, Value::Empty), "right of it");
446 assert_eq!(d.get(2, 3), Some(&kinds[1]));
447 assert_eq!(d.get(3, 3), Some(&Value::Empty), "a cell holding nothing");
448 assert_eq!(d.get(5, 2), None, "outside");
449 assert_eq!(d.get(2, 1), None);
450 assert_eq!(walk(&d, rect(2, 2, 4, 4)), kinds.to_vec());
453 assert_eq!(walk(&d, rect(1, 1, MAX_ROW, MAX_COLUMN)), kinds.to_vec());
454 assert_eq!(
455 walk(&d, rect(3, 3, 9, 9)),
456 vec![kinds[4].clone(), n(7.0), n(8.0)]
457 );
458 assert_eq!(walk(&d, rect(1, 3, 3, 3)), vec![kinds[1].clone()]);
459 assert_eq!(walk(&d, rect(3, 3, 3, 3)), Vec::<Value>::new());
460 assert_eq!(walk(&d, rect(5, 1, 9, 9)), Vec::<Value>::new());
461 assert_eq!(walk(&d, rect(1, 5, 1, 9)), Vec::<Value>::new());
462 let mut seen = 0;
464 let stopped = d.each(&rect(2, 2, 4, 4), |v| {
465 seen += 1;
466 if matches!(v, Value::Bool(_)) {
467 Err("stop")
468 } else {
469 Ok(())
470 }
471 });
472 assert_eq!((stopped, seen), (Err("stop"), 3));
473 }
474
475 #[test]
476 fn a_store_keeps_its_memory_and_refuses_what_passes_its_limit() {
477 let extent = Some(((1, 1), (10, 10)));
478 let mut d = Dense::over(extent, 100, None, true).unwrap();
479 assert!(d.set(10, 10, Value::Text("kept".to_string())));
480 let at = d.values.as_ptr();
481 let Some(Value::Text(text)) = d.get(10, 10) else {
482 panic!("a text")
483 };
484 let text_at = text.as_ptr();
485 let same = Dense::over(extent, 100, Some(d), true).unwrap();
488 assert_eq!(same.values.as_ptr(), at);
489 let Some(Value::Text(text)) = same.get(10, 10) else {
490 panic!("the text kept")
491 };
492 assert_eq!(text.as_ptr(), text_at, "the same text, not a copy");
493 let again = Dense::over(Some(((3, 3), (5, 5))), 100, Some(same), true).unwrap();
495 assert_eq!(again.values.as_ptr(), at, "the old store's memory");
496 assert_eq!(again.values.len(), 9);
497 assert!(again.values.iter().all(|v| *v == Value::Empty));
498 assert_eq!(again.get(3, 3), Some(&Value::Empty));
499 assert!(Dense::over(Some(((1, 1), (10, 10))), 99, None, true).is_none());
500 assert!(
501 Dense::over(None, 100, None, true).is_none(),
502 "a sheet with no cell"
503 );
504 assert!(Dense::over(Some(((1, 1), (MAX_ROW, MAX_COLUMN))), 1 << 20, None, true).is_none());
506 }
507
508 fn slice_of(d: &Dense, row: u32, col: u32, n: usize) -> Option<Vec<f64>> {
512 (0..n)
513 .map(|j| match d.get(row, col + j as u32)? {
514 Value::Number(x) => Some(*x),
515 _ => None,
516 })
517 .collect()
518 }
519
520 #[test]
521 fn the_cells_a_reference_keeps_inside_are_those_it_leaves_nowhere() {
522 let d = Dense::over(Some(((2, 2), (6, 6))), 100, None, true).unwrap();
528 for (top, bottom) in [(2, 2), (3, 5), (1, 3), (6, 7), (2, 6)] {
529 for col in 0..=8u32 {
530 for width in 1..=4usize {
531 for n in 1..=7usize {
532 for moves in [true, false] {
533 let fits = |i: usize| {
534 let left = col + if moves { i as u32 } else { 0 };
535 let right = left + width as u32 - 1;
536 top >= 2 && bottom <= 6 && left >= 2 && right <= 6
537 };
538 let want: Vec<usize> = (0..n).filter(|&i| fits(i)).collect();
539 let (a, b) = d.inside((top, bottom), col, width, n, moves);
540 assert_eq!(
541 (a..b).collect::<Vec<_>>(),
542 want,
543 "rows {top}-{bottom}, column {col}, width {width}, {n} cells, moving {moves}"
544 );
545 }
546 }
547 }
548 }
549 }
550 }
551
552 fn column_of(d: &Dense, row: u32, col: u32, n: usize) -> Option<Vec<f64>> {
556 (0..n)
557 .map(|j| match d.get(row + j as u32, col)? {
558 Value::Number(x) => Some(*x),
559 _ => None,
560 })
561 .collect()
562 }
563
564 fn spelled(s: &Shadow) -> (Vec<u64>, &[u8], &[u32]) {
566 (
567 s.numbers.iter().map(|x| x.to_bits()).collect(),
568 &s.kinds,
569 &s.odd,
570 )
571 }
572
573 #[test]
574 fn a_store_s_numbers_are_its_slots_after_every_write() {
575 let mut seed: u64 = 0x5EED;
583 let mut next = |n: u64| {
584 seed = seed
585 .wrapping_mul(6_364_136_223_846_793_005)
586 .wrapping_add(1_442_695_040_888_963_407);
587 (seed >> 33) % n
588 };
589 let values = [
590 Value::Number(0.0),
591 Value::Number(-0.0),
592 Value::Number(1.5),
593 Value::Number(-3.0),
594 Value::Number(1e300),
595 Value::Text("x".to_string()),
596 Value::Bool(true),
597 Value::Error(ErrorKind::NA),
598 Value::Empty,
599 ];
600 let extents = [Some(((2, 3), (6, 9))), Some(((1, 1), (3, 4)))];
601 let mut d = Dense::over(extents[0], 100, None, true).unwrap();
602 let (mut kept, mut columns) = (0, 0);
603 for round in 0..2000 {
604 if round % 500 == 499 {
607 d = Dense::over(extents[next(2) as usize], 100, Some(d), round != 999).unwrap();
608 } else if round % 100 == 99 {
609 d.shade(round / 100 % 2 == 1);
610 }
611 let (top, left) = (d.top, d.left);
612 let (row, col) = (
613 top + next(d.height as u64) as u32,
614 left + next(d.width as u64) as u32,
615 );
616 let v = values[next(values.len() as u64) as usize].clone();
617 assert!(d.set(row, col, v));
618 if let Some(s) = &d.shadow {
619 kept += 1;
620 for r in 0..d.height {
621 let row = top + r as u32;
622 let odd = (0..d.width as u32)
623 .filter(|&c| !matches!(d.get(row, left + c), Some(Value::Number(_))))
624 .count();
625 assert_eq!(s.odd[r] as usize, odd, "round {round}, row {row}");
626 }
627 assert_eq!(
628 spelled(s),
629 spelled(&d.made(Shadow::default())),
630 "round {round}"
631 );
632 }
633 for _ in 0..4 {
634 let row = (top + next(d.height as u64 + 2) as u32).saturating_sub(1);
635 let col = (left + next(d.width as u64 + 2) as u32).saturating_sub(1);
636 let n = 1 + next(d.width as u64 + 1) as usize;
637 let got = d
638 .numbers_in_row(row, col, n)
639 .map(|s| s.iter().map(|x| x.to_bits()).collect::<Vec<_>>());
640 let want = slice_of(&d, row, col, n)
641 .filter(|_| d.shadow.is_some())
642 .map(|s| s.iter().map(|x| x.to_bits()).collect::<Vec<_>>());
643 assert_eq!(got, want, "round {round}: {n} slots from ({row}, {col})");
644 let n = 1 + next(d.height as u64 + 1) as usize;
647 let mut out = vec![7.0];
648 let read = d.numbers_in_column(row, col, n, &mut out);
649 let want = column_of(&d, row, col, n).filter(|_| d.shadow.is_some());
650 let got = read.then(|| out[1..].iter().map(|x| x.to_bits()).collect::<Vec<_>>());
651 assert_eq!(
652 got,
653 want.map(|s| s.iter().map(|x| x.to_bits()).collect::<Vec<_>>()),
654 "round {round}: {n} slots down from ({row}, {col})"
655 );
656 assert_eq!(out.len(), if read { 1 + n } else { 1 }, "round {round}");
657 columns += usize::from(read);
658 }
659 }
660 assert!(
661 0 < kept && kept < 2000,
662 "rounds with numbers and without: {kept}"
663 );
664 assert!(columns > 0, "a column read whole somewhere");
665 }
666}