1use super::blocks::{
11 self, BlockWrite, Consensus, Deferred, Grid, Output, CONSENSUS_BLOCK_BYTES, FALLBACK_BYTES,
12 OUTPUT_BLOCK_BYTES,
13};
14use super::controls::{self, ControlTracks};
15use super::locate::{self, Cells, TierMap};
16use super::model::{self, LayerModel};
17use super::stats::Acc;
18use super::stats::{CellMap, LayerStats};
19use super::{tolerance, CanvasView, Footprint, Frame, BLOCK, CELL, TIER_FRINGE};
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub enum Pass {
23 Learn,
24 Agree,
25 Relearn,
26 Write,
27}
28
29#[derive(Clone, Debug, PartialEq)]
31pub struct Decision {
32 pub layer: usize,
33 pub canvas: usize,
34 pub x: i64,
35 pub y: i64,
36 pub occlusions: Vec<(i64, i64, i64, i64)>,
37}
38
39pub fn admitted(plans: &[Vec<(Decision, bool)>]) -> Vec<Vec<bool>> {
46 #[derive(Clone, Copy, PartialEq)]
47 enum Still {
48 Unseen,
49 At(usize, i64, i64),
50 Moved,
51 }
52 let layers = plans
53 .iter()
54 .flatten()
55 .map(|(d, _)| d.layer + 1)
56 .max()
57 .unwrap_or(0);
58 let mut still = vec![Still::Unseen; layers];
59 for (d, _) in plans.iter().flatten().filter(|(_, blind)| !blind) {
60 let at = Still::At(d.canvas, d.x, d.y);
61 let s = &mut still[d.layer];
62 *s = if *s == Still::Unseen || *s == at {
63 at
64 } else {
65 Still::Moved
66 };
67 }
68 plans
69 .iter()
70 .map(|ds| {
71 ds.iter()
72 .map(|(d, blind)| !blind || still[d.layer] == Still::At(d.canvas, d.x, d.y))
73 .collect()
74 })
75 .collect()
76}
77
78pub struct Layer {
79 controls: Option<ControlTracks>,
80 pub x: i64,
81 pub y: i64,
82 pub width: usize,
83 pub height: usize,
84 pub code: u8,
86 labels: Option<Vec<u8>>,
88 stats: Option<LayerStats>,
89 pub model: Option<LayerModel>,
90 zone: Vec<bool>,
91 last: Option<(usize, i64, i64)>,
93 pending: Option<(u32, Decision, u32)>,
95}
96
97impl Layer {
98 fn label_mask(
101 &self,
102 labels: Option<&[u8]>,
103 frame_width: usize,
104 frame_height: usize,
105 ) -> Vec<u8> {
106 let mut m = vec![0u8; self.width * self.height];
107 for ly in 0..self.height {
108 let sy = self.y + ly as i64;
109 if sy < 0 || sy >= frame_height as i64 {
110 continue;
111 }
112 for lx in 0..self.width {
113 let sx = self.x + lx as i64;
114 if sx < 0 || sx >= frame_width as i64 {
115 continue;
116 }
117 m[ly * self.width + lx] = match labels {
118 Some(l) if self.code != 0 => {
119 (l[sy as usize * frame_width + sx as usize] == self.code) as u8
120 }
121 _ => 1,
122 };
123 }
124 }
125 m
126 }
127}
128
129pub struct Canvas {
130 pub x: i64,
131 pub y: i64,
132 pub width: usize,
133 pub height: usize,
134 pub main: CellMap,
135 pub consensus: Option<Consensus>,
136 pub output: Option<Output>,
137}
138
139#[derive(Default, Clone, Copy, Debug)]
140pub struct Summary {
141 pub objects: usize,
142 pub components: usize,
143 pub coherent: usize,
144 pub component_blocks: usize,
145 pub provisional: usize,
146 pub fallback: usize,
147}
148
149pub struct Reveal {
150 pub width: usize,
151 pub height: usize,
152 pub tau: i32,
153 pub pass: Pass,
154 pub layers: Vec<Layer>,
155 pub canvases: Vec<Canvas>,
156 pub writes: Vec<BlockWrite>,
158}
159
160fn layer_mask<'a>(l: &'a Layer, d: &Decision) -> std::borrow::Cow<'a, [u8]> {
163 let base = l.labels.as_deref().unwrap_or(&[]);
164 if d.occlusions.is_empty() {
165 std::borrow::Cow::Borrowed(base)
166 } else {
167 std::borrow::Cow::Owned(Footprint::occlude(
168 base,
169 l.x,
170 l.y,
171 l.width,
172 l.height,
173 &d.occlusions,
174 ))
175 }
176}
177
178fn footprint<'a>(l: &Layer, mask: &'a [u8]) -> Footprint<'a> {
179 Footprint {
180 x: l.x,
181 y: l.y,
182 width: l.width,
183 height: l.height,
184 mask,
185 }
186}
187
188pub fn canvas_bytes(width: usize, height: usize) -> usize {
193 let cells = (width / CELL + 2) * (height / CELL + 2);
194 let blocks = (width / BLOCK + 2) * (height / BLOCK + 2);
195 cells * std::mem::size_of::<[u8; 4]>() + blocks * (CONSENSUS_BLOCK_BYTES + OUTPUT_BLOCK_BYTES)
196}
197
198pub fn layer_bytes(width: usize, height: usize) -> usize {
201 let cells = width.div_ceil(CELL) * height.div_ceil(CELL);
202 let blocks = (width / BLOCK + 2) * (height / BLOCK + 2);
203 width * height
205 + cells * (std::mem::size_of::<Acc>() + 8 * std::mem::size_of::<u64>())
206 + blocks * blocks::CELLS * std::mem::size_of::<[u8; 3]>()
207 + controls::memory_bound(cells)
208}
209
210impl Reveal {
211 pub fn new(width: usize, height: usize, noise: f64) -> Self {
212 Self {
213 width,
214 height,
215 tau: tolerance(noise),
216 pass: Pass::Learn,
217 layers: Vec::new(),
218 canvases: Vec::new(),
219 writes: Vec::new(),
220 }
221 }
222
223 pub fn add_layer(&mut self, x: i64, y: i64, width: usize, height: usize, code: u8) -> usize {
224 self.layers.push(Layer {
225 controls: Some(ControlTracks::new(
226 width.div_ceil(CELL) * height.div_ceil(CELL),
227 )),
228 x,
229 y,
230 width,
231 height,
232 code,
233 labels: None,
234 stats: None,
235 model: None,
236 zone: Vec::new(),
237 last: None,
238 pending: None,
239 });
240 self.layers.len() - 1
241 }
242
243 pub fn add_canvas(&mut self, x: i64, y: i64, width: usize, height: usize) -> usize {
244 self.canvases.push(Canvas {
245 x,
246 y,
247 width,
248 height,
249 main: CellMap::covering(x, y, width, height),
250 consensus: None,
251 output: None,
252 });
253 self.canvases.len() - 1
254 }
255
256 pub fn fallback_bytes(&self) -> usize {
259 self.canvases
260 .iter()
261 .filter_map(|c| c.consensus.as_ref())
262 .map(|c| c.unseen_blocks() * FALLBACK_BYTES)
263 .sum()
264 }
265
266 pub fn ingest(&mut self, canvas: usize, view: &CanvasView<'_>) {
268 self.canvases[canvas].main.ingest(view);
269 }
270
271 pub fn begin(&mut self, pass: Pass) {
273 match pass {
274 Pass::Learn | Pass::Relearn => {
275 for l in &mut self.layers {
276 l.stats = Some(LayerStats::new(l.x, l.y, l.width, l.height));
277 l.last = None;
278 }
279 }
280 Pass::Agree | Pass::Write => {
281 for l in &mut self.layers {
282 if let Some(s) = l.stats.take() {
283 let m = model::build(&s, self.tau);
284 l.zone = m.overlay_zone();
285 l.model = Some(m);
286 }
287 l.pending = None;
288 }
289 for c in &mut self.canvases {
290 if pass == Pass::Agree {
291 c.consensus =
292 Some(Consensus::new(Grid::covering(c.x, c.y, c.width, c.height)));
293 } else if let Some(cons) = &c.consensus {
294 c.output = Some(Output::new(cons, blocks::components(cons)));
295 }
296 }
297 }
298 }
299 self.pass = pass;
300 self.writes.clear();
301 }
302
303 pub fn fallback_blocks(&self, canvas: usize) -> Vec<(i64, i64)> {
306 let c = &self.canvases[canvas];
307 let (Some(cons), Some(out)) = (&c.consensus, &c.output) else {
308 return Vec::new();
309 };
310 out.fallback_blocks()
311 .into_iter()
312 .map(|b| {
313 (
314 cons.grid.gx + (b % cons.grid.bw) as i64,
315 cons.grid.gy + (b / cons.grid.bw) as i64,
316 )
317 })
318 .collect()
319 }
320
321 pub fn set_main(&mut self, canvas: usize, gx: i64, gy: i64, rgba: &[u8]) {
322 let c = &mut self.canvases[canvas];
323 let (Some(cons), Some(out)) = (&c.consensus, &mut c.output) else {
324 return;
325 };
326 let (bx, by) = (gx - cons.grid.gx, gy - cons.grid.gy);
327 if bx >= 0 && by >= 0 && (bx as usize) < cons.grid.bw && (by as usize) < cons.grid.bh {
328 out.set_main(by as usize * cons.grid.bw + bx as usize, rgba);
329 }
330 }
331
332 pub fn wanted(&self, plans: &[Vec<Decision>]) -> Vec<bool> {
338 let n = plans.len();
339 let mut want = vec![false; n];
340 let key = |d: &Decision| (d.canvas, d.x, d.y);
341 match self.pass {
342 Pass::Learn | Pass::Relearn => {
343 let mut last = vec![None; self.layers.len()];
344 for (i, ds) in plans.iter().enumerate() {
345 for d in ds {
346 if last[d.layer] != Some(key(d)) {
347 last[d.layer] = Some(key(d));
348 want[i] = true;
349 }
350 }
351 }
352 }
353 Pass::Agree | Pass::Write => {
354 let mut run = vec![0u32; self.layers.len()];
355 let mut prev: Vec<Option<(usize, i64, i64)>> = vec![None; self.layers.len()];
356 for (i, ds) in plans.iter().enumerate() {
357 let mut seen = vec![None; self.layers.len()];
358 for d in ds {
359 seen[d.layer] = Some(key(d));
360 }
361 for (l, k) in seen.iter().enumerate() {
362 run[l] = match k {
363 Some(k) if prev[l] == Some(*k) => run[l] + 1,
364 Some(_) => 1,
365 None => 0,
366 };
367 let Some(k) = k else { continue };
368 let continues = plans
369 .get(i + 1)
370 .is_some_and(|next| next.iter().any(|d| d.layer == l && key(d) == *k));
371 if !continues {
372 want[i] = true;
373 if run[l] >= 2 {
374 want[i - 1] = true;
375 }
376 }
377 }
378 prev = seen;
379 }
380 }
381 }
382 want
383 }
384
385 fn ensure_labels(&mut self, labels: Option<&[u8]>) {
387 let (w, h) = (self.width, self.height);
388 for l in &mut self.layers {
389 if l.labels.is_none() {
390 l.labels = Some(l.label_mask(labels, w, h));
391 }
392 }
393 }
394
395 pub fn frame(
398 &mut self,
399 index: u32,
400 current: Option<&[u8]>,
401 prev: Option<&[u8]>,
402 prev2: Option<&[u8]>,
403 labels: Option<&[u8]>,
404 decisions: &[Decision],
405 ) {
406 self.ensure_labels(labels);
407 match self.pass {
408 Pass::Learn | Pass::Relearn => {
409 let Some(current) = current else { return };
411 let cur = Frame {
412 rgba: current,
413 width: self.width,
414 height: self.height,
415 };
416 for d in decisions {
417 let key = (d.canvas, d.x, d.y);
418 if self.layers[d.layer].last == Some(key) {
419 continue;
420 }
421 self.layers[d.layer].last = Some(key);
422 let mut stats = self.layers[d.layer].stats.take().unwrap();
424 let layer = &self.layers[d.layer];
425 let mask = layer_mask(layer, d);
426 let fp = footprint(layer, &mask);
427 let c = &self.canvases[d.canvas];
428 match (self.pass, &c.consensus) {
429 (Pass::Learn, _) => {
430 stats.accumulate_cells(&cur, &fp, &c.main, (d.x, d.y), self.tau);
431 }
432 (_, Some(cons)) => {
433 stats.accumulate_cells(&cur, &fp, cons, (d.x, d.y), self.tau);
434 }
435 _ => {}
436 }
437 drop(mask);
438 self.layers[d.layer].stats = Some(stats);
439 }
440 }
441 Pass::Agree | Pass::Write => {
442 let mut fresh = Vec::with_capacity(decisions.len());
444 for d in decisions {
445 let run = match &self.layers[d.layer].pending {
446 Some((pt, pd, len))
447 if *pt + 1 == index
448 && (pd.canvas, pd.x, pd.y) == (d.canvas, d.x, d.y) =>
449 {
450 len + 1
451 }
452 _ => 1,
453 };
454 fresh.push((d.clone(), run));
455 }
456 for l in 0..self.layers.len() {
458 let Some((pt, pd, _)) = &self.layers[l].pending else {
459 continue;
460 };
461 let continues = decisions.iter().any(|d| {
462 d.layer == l
463 && *pt + 1 == index
464 && (d.canvas, d.x, d.y) == (pd.canvas, pd.x, pd.y)
465 });
466 if continues {
467 continue;
468 }
469 let (pt, pd, len) = self.layers[l].pending.take().unwrap();
470 if let Some(p) = prev {
471 self.vote(pt, &pd, p, if len >= 2 { prev2 } else { None }, labels);
472 }
473 }
474 for (d, run) in fresh {
475 let l = d.layer;
476 self.layers[l].pending = Some((index, d, run));
477 }
478 }
479 }
480 }
481
482 pub fn end(&mut self, last: Option<&[u8]>, before: Option<&[u8]>, labels: Option<&[u8]>) {
485 let (true, Some(last)) = (matches!(self.pass, Pass::Agree | Pass::Write), last) else {
486 return;
487 };
488 for l in 0..self.layers.len() {
489 if let Some((pt, pd, len)) = self.layers[l].pending.take() {
490 self.vote(pt, &pd, last, if len >= 2 { before } else { None }, labels);
491 }
492 }
493 }
494
495 fn vote(
496 &mut self,
497 index: u32,
498 d: &Decision,
499 rgba: &[u8],
500 before: Option<&[u8]>,
501 labels: Option<&[u8]>,
502 ) {
503 let (w, h, tau) = (self.width, self.height, self.tau);
504 let frame = Frame {
505 rgba,
506 width: w,
507 height: h,
508 };
509 let Some(m) = self.layers[d.layer].model.as_mut() else {
510 return;
511 };
512 let cells =
513 (self.pass == Pass::Agree && controls::needs_cells(m)).then(|| Cells::bin(&frame, m));
514 let mut tiers = match &cells {
515 Some(cells) => locate::classify(cells, m, tau),
516 None => locate::tiers(&frame, m, tau),
517 };
518 let mut tracks = self.layers[d.layer].controls.take().unwrap();
519 let layer = &self.layers[d.layer];
520 let m = layer.model.as_ref().unwrap();
521 let mask = layer_mask(layer, d);
522 let fp = footprint(layer, &mask);
523 if let Some(cells) = &cells {
524 tracks.observe(
525 cells,
526 m,
527 &fp,
528 tau,
529 controls::View {
530 index,
531 canvas: d.canvas,
532 pose: (d.x, d.y),
533 },
534 );
535 }
536 if self.pass == Pass::Write {
537 tracks.apply(index, m, &layer.zone, &mut tiers);
538 }
539 if let Some(b) = before {
540 let noise = (self.tau / 4).max(2);
541 for cy in 0..m.ch {
542 for cx in 0..m.cw {
543 let i = cy * m.cw + cx;
544 if !layer.zone[i] || tiers[i] <= TIER_FRINGE {
545 continue;
546 }
547 let (sx, sy) = (m.x + (cx * CELL) as i64, m.y + (cy * CELL) as i64);
548 'cell: for dy in 0..CELL as i64 {
549 for dx in 0..CELL as i64 {
550 let (x, y) = (sx + dx, sy + dy);
551 if x < 0 || y < 0 || x >= w as i64 || y >= h as i64 {
552 continue;
553 }
554 let j = (y as usize * w + x as usize) * 4;
555 if (0..3).any(|c| (rgba[j + c] as i32 - b[j + c] as i32).abs() > noise)
556 {
557 tiers[i] = TIER_FRINGE;
558 break 'cell;
559 }
560 }
561 }
562 }
563 }
564 }
565 let tm = TierMap {
566 x: m.x,
567 y: m.y,
568 cw: m.cw,
569 ch: m.ch,
570 tier: &tiers,
571 };
572 let _ = labels;
573 let c = &mut self.canvases[d.canvas];
574 match self.pass {
575 Pass::Agree => {
576 if let Some(cons) = c.consensus.as_mut() {
577 cons.observe(&frame, &fp, &tm, (d.x, d.y));
578 }
579 }
580 Pass::Write => {
581 if let (Some(cons), Some(out)) = (c.consensus.as_ref(), c.output.as_mut()) {
582 let start = self.writes.len();
583 let still_previous = before.map(|rgba| Frame {
584 rgba,
585 width: w,
586 height: h,
587 });
588 out.emit(
589 cons,
590 &frame,
591 still_previous.as_ref(),
592 &fp,
593 &tm,
594 (d.x, d.y),
595 tau,
596 index,
597 &c.main,
598 &mut self.writes,
599 );
600 for w in &mut self.writes[start..] {
601 w.canvas = d.canvas as u32;
602 }
603 }
604 }
605 _ => {}
606 }
607 drop(mask);
608 self.layers[d.layer].controls = Some(tracks);
609 }
610
611 pub fn take_writes(&mut self) -> Vec<BlockWrite> {
614 std::mem::take(&mut self.writes)
615 }
616
617 pub fn finish(&self, canvas: usize) -> Vec<Deferred> {
619 let c = &self.canvases[canvas];
620 match (&c.consensus, &c.output) {
621 (Some(cons), Some(out)) => out.finish(cons, self.tau),
622 _ => Vec::new(),
623 }
624 }
625
626 pub fn provenance(
629 &self,
630 canvas: usize,
631 deferred: &[blocks::Deferred],
632 ) -> Option<(i64, i64, usize, usize, Vec<i32>)> {
633 let c = &self.canvases[canvas];
634 let (cons, out) = (c.consensus.as_ref()?, c.output.as_ref()?);
635 let g = cons.grid;
636 Some((g.gx, g.gy, g.bw, g.bh, out.sources(cons, deferred)))
637 }
638
639 pub fn summary(&self) -> Summary {
640 let mut s = Summary {
641 objects: self
642 .layers
643 .iter()
644 .filter_map(|l| l.model.as_ref())
645 .map(|m| m.objects.len())
646 .sum(),
647 ..Default::default()
648 };
649 for c in &self.canvases {
650 if let Some(out) = &c.output {
651 s.components += out.components.len();
652 for k in &out.kind {
653 match k {
654 blocks::Kind::Coherent | blocks::Kind::Edge => s.coherent += 1,
655 blocks::Kind::Component => s.component_blocks += 1,
656 blocks::Kind::Provisional => s.provisional += 1,
657 blocks::Kind::Fallback => s.fallback += 1,
658 blocks::Kind::None => {}
659 }
660 }
661 }
662 }
663 s
664 }
665}
666
667#[cfg(test)]
668mod tests {
669 use super::{admitted, canvas_bytes, layer_bytes, Decision, Pass, Reveal};
670 use std::alloc::{GlobalAlloc, Layout, System};
671 use std::cell::Cell;
672
673 thread_local! {
676 static LIVE: Cell<isize> = const { Cell::new(0) };
677 }
678 struct Counting;
679 unsafe impl GlobalAlloc for Counting {
680 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
681 let _ = LIVE.try_with(|l| l.set(l.get() + layout.size() as isize));
682 System.alloc(layout)
683 }
684 unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
685 let _ = LIVE.try_with(|l| l.set(l.get() + layout.size() as isize));
686 System.alloc_zeroed(layout)
687 }
688 unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
689 let _ = LIVE.try_with(|l| l.set(l.get() - layout.size() as isize));
690 System.dealloc(ptr, layout)
691 }
692 unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new: usize) -> *mut u8 {
693 let _ = LIVE.try_with(|l| l.set(l.get() + new as isize - layout.size() as isize));
694 System.realloc(ptr, layout, new)
695 }
696 }
697 #[global_allocator]
698 static ALLOCATOR: Counting = Counting;
699 fn live() -> isize {
700 LIVE.with(|l| l.get())
701 }
702
703 #[test]
708 fn the_estimates_cover_what_the_reveal_holds() {
709 for (x, y, w, h) in [(0, 0, 320, 240), (-7, 13, 3001, 2002), (5, -3, 17, 4099)] {
710 let before = live();
711 let mut r = Reveal::new(w, h, 10.0);
712 r.add_layer(0, 0, w, h, 0);
713 r.add_canvas(x, y, w, h);
714 r.begin(Pass::Learn);
715 r.begin(Pass::Agree);
716 r.begin(Pass::Relearn);
717 let fallback = r.fallback_bytes();
718 r.begin(Pass::Write);
719 let held = (live() - before) as usize;
720 let estimate = canvas_bytes(w, h) + layer_bytes(w, h) + fallback;
721 assert!(
722 held <= estimate,
723 "{w}x{h}: holds {held} bytes, estimated {estimate}"
724 );
725 assert!(fallback > 0, "{w}x{h}");
727 drop(r);
728 }
729 let per_pixel = canvas_bytes(3001, 2002) as f64 / (3001.0 * 2002.0);
732 assert!(
733 (4.0..7.0).contains(&per_pixel),
734 "{per_pixel} bytes per canvas pixel"
735 );
736 }
737
738 fn d(layer: usize, canvas: usize, x: i64, y: i64) -> Decision {
739 Decision {
740 layer,
741 canvas,
742 x,
743 y,
744 occlusions: Vec::new(),
745 }
746 }
747
748 fn admit(plans: &[&[(Decision, bool)]]) -> Vec<Vec<bool>> {
750 let plans: Vec<Vec<(Decision, bool)>> = plans.iter().map(|p| p.to_vec()).collect();
751 admitted(&plans)
752 }
753
754 #[test]
755 fn tracked_decisions_always_count() {
756 let got = admit(&[
757 &[(d(0, 0, 0, 0), false)],
758 &[(d(0, 0, 0, 40), false)],
759 &[(d(0, 0, 0, 80), false)],
760 ]);
761 assert_eq!(got, vec![vec![true], vec![true], vec![true]]);
762 }
763
764 #[test]
765 fn blind_frames_of_a_still_layer_count_at_its_pose() {
766 let got = admit(&[
768 &[(d(1, 0, 0, 0), true)],
769 &[(d(1, 0, 0, 0), false)],
770 &[(d(1, 0, 0, 0), false)],
771 &[(d(1, 0, 0, 0), true)],
772 &[(d(1, 0, 0, 0), true)],
773 ]);
774 assert_eq!(got, vec![vec![true]; 5]);
775 }
776
777 #[test]
778 fn a_blind_frame_at_another_pose_does_not_count() {
779 let got = admit(&[
780 &[(d(0, 0, 5, 5), false)],
781 &[(d(0, 0, 5, 6), true)],
782 &[(d(0, 1, 5, 5), true)],
783 ]);
784 assert_eq!(got, vec![vec![true], vec![false], vec![false]]);
785 }
786
787 #[test]
788 fn blind_frames_of_a_layer_that_moved_do_not_count() {
789 let got = admit(&[
791 &[(d(0, 0, 0, 0), false)],
792 &[(d(0, 0, 0, 30), false)],
793 &[(d(0, 0, 0, 30), true)],
794 &[(d(0, 0, 0, 30), true)],
795 ]);
796 assert_eq!(got, vec![vec![true], vec![true], vec![false], vec![false]]);
797 }
798
799 #[test]
800 fn blind_frames_of_a_layer_never_tracked_do_not_count() {
801 let got = admit(&[&[(d(0, 0, 0, 0), true)], &[(d(0, 0, 0, 0), true)]]);
802 assert_eq!(got, vec![vec![false], vec![false]]);
803 }
804
805 #[test]
806 fn each_layer_is_judged_on_its_own() {
807 let got = admit(&[
808 &[(d(0, 0, 0, 0), false), (d(1, 1, 0, 0), false)],
809 &[(d(0, 0, 0, 50), false), (d(1, 1, 0, 0), true)],
810 &[(d(0, 0, 0, 50), true), (d(1, 1, 0, 0), true)],
811 ]);
812 assert_eq!(
813 got,
814 vec![vec![true, true], vec![true, true], vec![false, true]]
815 );
816 }
817}