1use super::op::{BinaryOpcode, UnaryOpcode};
3use crate::{context::NotAVar, var::Var};
4use ordered_float::OrderedFloat;
5use std::{cmp::Ordering, sync::Arc};
6
7#[derive(Debug)]
13#[allow(missing_docs)]
14pub enum TreeOp {
15 Input(Var),
17 Const(f32),
18 Binary(BinaryOpcode, Arc<TreeOp>, Arc<TreeOp>),
19 Unary(UnaryOpcode, Arc<TreeOp>),
20 RemapAxes {
28 target: Arc<TreeOp>,
29 x: Arc<TreeOp>,
30 y: Arc<TreeOp>,
31 z: Arc<TreeOp>,
32 },
33 RemapAffine {
38 target: Arc<TreeOp>,
39 mat: nalgebra::Affine3<f32>,
40 },
41}
42
43impl Drop for TreeOp {
44 fn drop(&mut self) {
45 if self.eligible_for_fast_drop() {
47 return;
48 }
49
50 let mut todo = vec![std::mem::replace(self, TreeOp::Const(0.0))];
51 let empty = Arc::new(TreeOp::Const(0.0));
52 while let Some(mut t) = todo.pop() {
53 for t in t.iter_children_mut() {
54 let arg = std::mem::replace(t, empty.clone());
55 todo.extend(Arc::into_inner(arg));
56 }
57 drop(t);
58 }
59 }
60}
61
62impl PartialEq for TreeOp {
73 fn eq(&self, other: &Self) -> bool {
74 if std::ptr::eq(self, other) {
75 return true;
76 }
77 let mut todo = vec![(self, other)];
79 while let Some((a, b)) = todo.pop() {
80 if std::ptr::eq(a, b) {
82 continue;
83 }
84 match (a, b) {
86 (TreeOp::Input(a), TreeOp::Input(b)) => {
87 if *a != *b {
88 return false;
89 }
90 }
91 (TreeOp::Const(a), TreeOp::Const(b)) => {
92 if OrderedFloat(*a) != OrderedFloat(*b) {
93 return false;
94 }
95 }
96 (TreeOp::Unary(op_a, ..), TreeOp::Unary(op_b, ..)) => {
97 if *op_a != *op_b {
98 return false;
99 }
100 }
101 (TreeOp::Binary(op_a, ..), TreeOp::Binary(op_b, ..)) => {
102 if *op_a != *op_b {
103 return false;
104 }
105 }
106 (TreeOp::RemapAxes { .. }, TreeOp::RemapAxes { .. }) => {
107 }
109 (
110 TreeOp::RemapAffine { mat: mat_a, .. },
111 TreeOp::RemapAffine { mat: mat_b, .. },
112 ) => {
113 if mat_a
114 .matrix()
115 .iter()
116 .zip(mat_b.matrix().iter())
117 .any(|(a, b)| OrderedFloat(*a) != OrderedFloat(*b))
118 {
119 return false;
120 }
121 }
122 _ => return false,
123 }
124 todo.extend(
125 a.iter_children()
126 .zip(b.iter_children())
127 .map(|(a, b)| (a.as_ref(), b.as_ref())),
128 )
129 }
130 true
131 }
132}
133
134impl Eq for TreeOp {}
135
136impl TreeOp {
137 fn eligible_for_fast_drop(&self) -> bool {
143 self.iter_children().all(|c| c.does_not_recurse())
144 }
145
146 fn does_not_recurse(&self) -> bool {
148 matches!(self, TreeOp::Const(..) | TreeOp::Input(..))
149 }
150
151 fn iter_children(&self) -> impl Iterator<Item = &Arc<TreeOp>> {
152 match self {
153 TreeOp::Const(..) | TreeOp::Input(..) => [None, None, None, None],
154 TreeOp::Unary(_op, arg) => [Some(arg), None, None, None],
155 TreeOp::Binary(_op, lhs, rhs) => [Some(lhs), Some(rhs), None, None],
156 TreeOp::RemapAxes { target, x, y, z } => {
157 [Some(target), Some(x), Some(y), Some(z)]
158 }
159 TreeOp::RemapAffine { target, .. } => {
160 [Some(target), None, None, None]
161 }
162 }
163 .into_iter()
164 .flatten()
165 }
166
167 fn iter_children_mut(&mut self) -> impl Iterator<Item = &mut Arc<TreeOp>> {
168 match self {
169 TreeOp::Const(..) | TreeOp::Input(..) => [None, None, None, None],
170 TreeOp::Unary(_op, arg) => [Some(arg), None, None, None],
171 TreeOp::Binary(_op, lhs, rhs) => [Some(lhs), Some(rhs), None, None],
172 TreeOp::RemapAxes { target, x, y, z } => {
173 [Some(target), Some(x), Some(y), Some(z)]
174 }
175 TreeOp::RemapAffine { target, .. } => {
176 [Some(target), None, None, None]
177 }
178 }
179 .into_iter()
180 .flatten()
181 }
182}
183
184impl std::hash::Hash for TreeOp {
185 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
186 let mut todo = vec![self];
187 while let Some(t) = todo.pop() {
188 std::mem::discriminant(t).hash(state);
189 match t {
190 TreeOp::Input(i) => i.hash(state),
191 TreeOp::Const(v) => OrderedFloat(*v).hash(state),
192 TreeOp::Binary(op, _lhs, _rhs) => op.hash(state),
193 TreeOp::Unary(op, _arg) => op.hash(state),
194 TreeOp::RemapAxes { .. } => (), TreeOp::RemapAffine { target: _, mat } => mat
196 .matrix()
197 .iter()
198 .for_each(|f| OrderedFloat(*f).hash(state)),
199 }
200 todo.extend(t.iter_children().map(|t| t.as_ref()))
201 }
202 }
203}
204
205impl From<f32> for Tree {
206 fn from(v: f32) -> Tree {
207 Tree::constant(v)
208 }
209}
210
211impl From<i32> for Tree {
212 fn from(v: i32) -> Tree {
213 Tree::constant(v as f32)
214 }
215}
216
217impl From<Var> for Tree {
218 fn from(v: Var) -> Tree {
219 Tree(Arc::new(TreeOp::Input(v)))
220 }
221}
222
223impl From<TreeOp> for Tree {
224 fn from(t: TreeOp) -> Tree {
225 Tree(Arc::new(t))
226 }
227}
228
229#[derive(Clone, Debug, PartialEq, Eq, Hash, facet::Facet)]
239pub struct Tree(#[facet(opaque)] Arc<TreeOp>);
240
241impl std::ops::Deref for Tree {
242 type Target = TreeOp;
243 fn deref(&self) -> &Self::Target {
244 &self.0
245 }
246}
247
248impl Tree {
249 pub fn axes() -> (Self, Self, Self) {
251 (Self::x(), Self::y(), Self::z())
252 }
253
254 pub fn as_ptr(&self) -> *const TreeOp {
258 Arc::as_ptr(&self.0)
259 }
260
261 pub fn ptr_eq(&self, other: &Self) -> bool {
263 std::ptr::eq(self.as_ptr(), other.as_ptr())
264 }
265
266 pub(crate) fn arc(&self) -> &Arc<TreeOp> {
268 &self.0
269 }
270
271 pub fn remap_xyz(&self, x: Tree, y: Tree, z: Tree) -> Tree {
279 Self(Arc::new(TreeOp::RemapAxes {
280 target: self.0.clone(),
281 x: x.0,
282 y: y.0,
283 z: z.0,
284 }))
285 }
286
287 pub fn remap_affine(&self, mat: nalgebra::Affine3<f32>) -> Tree {
292 let out = match &*self.0 {
294 TreeOp::RemapAffine { target, mat: next } => TreeOp::RemapAffine {
295 target: target.clone(),
296 mat: next * mat,
297 },
298 _ => TreeOp::RemapAffine {
299 target: self.0.clone(),
300 mat,
301 },
302 };
303 Self(out.into())
304 }
305
306 pub fn var(&self) -> Option<Var> {
308 if let TreeOp::Input(v) = &*self.0 {
309 Some(*v)
310 } else {
311 None
312 }
313 }
314
315 pub fn deriv(&self, v: Var) -> Tree {
317 let mut ctx = crate::Context::new();
318 let node = ctx.import(self);
319 ctx.deriv(node, v).and_then(|d| ctx.export(d)).unwrap()
320 }
321
322 pub fn pow(&self, mut n: i64) -> Self {
324 let mut x = match n.cmp(&0) {
326 Ordering::Less => {
327 n = -n;
328 self.recip()
329 }
330 Ordering::Equal => {
331 return Tree::from(1.0);
332 }
333 Ordering::Greater => self.clone(),
334 };
335 let mut y: Option<Tree> = None;
336 while n > 1 {
337 if n % 2 == 1 {
338 y = match y {
339 Some(y) => Some(x.clone() * y),
340 None => Some(x.clone()),
341 };
342 n -= 1;
343 }
344 x = x.square();
345 n /= 2;
346 }
347 if let Some(y) = y {
348 x *= y;
349 }
350 x
351 }
352}
353
354impl TryFrom<Tree> for Var {
355 type Error = NotAVar;
356 fn try_from(t: Tree) -> Result<Var, NotAVar> {
357 t.var().ok_or(NotAVar)
358 }
359}
360
361#[allow(missing_docs)]
363impl Tree {
364 pub fn x() -> Self {
365 Tree(Arc::new(TreeOp::Input(Var::X)))
366 }
367 pub fn y() -> Self {
368 Tree(Arc::new(TreeOp::Input(Var::Y)))
369 }
370 pub fn z() -> Self {
371 Tree(Arc::new(TreeOp::Input(Var::Z)))
372 }
373 pub fn constant(f: f32) -> Self {
374 Tree(Arc::new(TreeOp::Const(f)))
375 }
376 fn op_unary(a: Tree, op: UnaryOpcode) -> Self {
377 Tree(Arc::new(TreeOp::Unary(op, a.0)))
378 }
379 fn op_binary(a: Tree, b: Tree, op: BinaryOpcode) -> Self {
380 Tree(Arc::new(TreeOp::Binary(op, a.0, b.0)))
381 }
382 pub fn square(&self) -> Self {
383 Self::op_unary(self.clone(), UnaryOpcode::Square)
384 }
385 pub fn floor(&self) -> Self {
386 Self::op_unary(self.clone(), UnaryOpcode::Floor)
387 }
388 pub fn ceil(&self) -> Self {
389 Self::op_unary(self.clone(), UnaryOpcode::Ceil)
390 }
391 pub fn round(&self) -> Self {
392 Self::op_unary(self.clone(), UnaryOpcode::Round)
393 }
394 pub fn sqrt(&self) -> Self {
395 Self::op_unary(self.clone(), UnaryOpcode::Sqrt)
396 }
397 pub fn max<T: Into<Tree>>(&self, other: T) -> Self {
398 Self::op_binary(self.clone(), other.into(), BinaryOpcode::Max)
399 }
400 pub fn min<T: Into<Tree>>(&self, other: T) -> Self {
401 Self::op_binary(self.clone(), other.into(), BinaryOpcode::Min)
402 }
403 pub fn compare<T: Into<Tree>>(&self, other: T) -> Self {
404 Self::op_binary(self.clone(), other.into(), BinaryOpcode::Compare)
405 }
406 pub fn mix<T: Into<Tree>>(&self, other: T) -> Self {
407 Self::op_binary(self.clone(), other.into(), BinaryOpcode::Mix)
408 }
409 pub fn modulo<T: Into<Tree>>(&self, other: T) -> Self {
410 Self::op_binary(self.clone(), other.into(), BinaryOpcode::Mod)
411 }
412 pub fn and<T: Into<Tree>>(&self, other: T) -> Self {
413 Self::op_binary(self.clone(), other.into(), BinaryOpcode::And)
414 }
415 pub fn or<T: Into<Tree>>(&self, other: T) -> Self {
416 Self::op_binary(self.clone(), other.into(), BinaryOpcode::Or)
417 }
418 pub fn atan2<T: Into<Tree>>(&self, other: T) -> Self {
419 Self::op_binary(self.clone(), other.into(), BinaryOpcode::Atan)
420 }
421 pub fn neg(&self) -> Self {
422 Self::op_unary(self.clone(), UnaryOpcode::Neg)
423 }
424 pub fn recip(&self) -> Self {
425 Self::op_unary(self.clone(), UnaryOpcode::Recip)
426 }
427 pub fn sin(&self) -> Self {
428 Self::op_unary(self.clone(), UnaryOpcode::Sin)
429 }
430 pub fn rand(&self) -> Tree {
431 Self::op_unary(self.clone(), UnaryOpcode::Rand)
432 }
433 pub fn cos(&self) -> Self {
434 Self::op_unary(self.clone(), UnaryOpcode::Cos)
435 }
436 pub fn tan(&self) -> Self {
437 Self::op_unary(self.clone(), UnaryOpcode::Tan)
438 }
439 pub fn asin(&self) -> Self {
440 Self::op_unary(self.clone(), UnaryOpcode::Asin)
441 }
442 pub fn acos(&self) -> Self {
443 Self::op_unary(self.clone(), UnaryOpcode::Acos)
444 }
445 pub fn atan(&self) -> Self {
446 Self::op_unary(self.clone(), UnaryOpcode::Atan)
447 }
448 pub fn exp(&self) -> Self {
449 Self::op_unary(self.clone(), UnaryOpcode::Exp)
450 }
451 pub fn ln(&self) -> Self {
452 Self::op_unary(self.clone(), UnaryOpcode::Ln)
453 }
454 pub fn not(&self) -> Self {
455 Self::op_unary(self.clone(), UnaryOpcode::Not)
456 }
457 pub fn abs(&self) -> Self {
458 Self::op_unary(self.clone(), UnaryOpcode::Abs)
459 }
460}
461
462macro_rules! impl_binary {
463 ($op:ident, $op_assign:ident, $base_fn:ident, $assign_fn:ident) => {
464 impl<A: Into<Tree>> std::ops::$op<A> for Tree {
465 type Output = Self;
466
467 fn $base_fn(self, other: A) -> Self {
468 Self::op_binary(self, other.into(), BinaryOpcode::$op)
469 }
470 }
471 impl<A: Into<Tree>> std::ops::$op_assign<A> for Tree {
472 fn $assign_fn(&mut self, other: A) {
473 use std::ops::$op;
474 let mut next = self.clone().$base_fn(other.into());
475 std::mem::swap(self, &mut next);
476 }
477 }
478 impl std::ops::$op<Tree> for f32 {
479 type Output = Tree;
480 fn $base_fn(self, other: Tree) -> Tree {
481 Tree::op_binary(self.into(), other, BinaryOpcode::$op)
482 }
483 }
484 };
485}
486
487impl_binary!(Add, AddAssign, add, add_assign);
488impl_binary!(Sub, SubAssign, sub, sub_assign);
489impl_binary!(Mul, MulAssign, mul, mul_assign);
490impl_binary!(Div, DivAssign, div, div_assign);
491
492impl std::ops::Neg for Tree {
493 type Output = Tree;
494 fn neg(self) -> Self::Output {
495 Tree::op_unary(self, UnaryOpcode::Neg)
496 }
497}
498
499#[cfg(test)]
500mod test {
501 use super::*;
502 use crate::Context;
503 use std::hash::BuildHasher;
504
505 #[test]
506 fn tree_x() {
507 let x1 = Tree::x();
508 let x2 = Tree::x();
509 assert!(!x1.ptr_eq(&x2)); assert_eq!(x1, x2); let mut ctx = Context::new();
513 let x1 = ctx.import(&x1);
514 let x2 = ctx.import(&x2);
515 assert_eq!(x1, x2);
516 }
517
518 #[test]
519 fn test_remap_xyz() {
520 let s = Tree::x() + 1.0;
522
523 let v = s.remap_xyz(Tree::y(), Tree::z(), Tree::x());
524 let mut ctx = Context::new();
525 let v_ = ctx.import(&v);
526 assert_eq!(ctx.eval_xyz(v_, 0.0, 1.0, 0.0).unwrap(), 2.0);
527
528 let v = s.remap_xyz(Tree::z(), Tree::x(), Tree::y());
529 let mut ctx = Context::new();
530 let v_ = ctx.import(&v);
531 assert_eq!(ctx.eval_xyz(v_, 0.0, 0.0, 1.0).unwrap(), 2.0);
532
533 let v = s.remap_xyz(Tree::x(), Tree::y(), Tree::z());
534 let mut ctx = Context::new();
535 let v_ = ctx.import(&v);
536 assert_eq!(ctx.eval_xyz(v_, 1.0, 0.0, 0.0).unwrap(), 2.0);
537
538 let s = Tree::y() + 1.0;
540
541 let v = s.remap_xyz(Tree::y(), Tree::z(), Tree::x());
542 let mut ctx = Context::new();
543 let v_ = ctx.import(&v);
544 assert_eq!(ctx.eval_xyz(v_, 0.0, 0.0, 1.0).unwrap(), 2.0);
545
546 let v = s.remap_xyz(Tree::z(), Tree::x(), Tree::y());
547 let mut ctx = Context::new();
548 let v_ = ctx.import(&v);
549 assert_eq!(ctx.eval_xyz(v_, 1.0, 0.0, 0.0).unwrap(), 2.0);
550
551 let v = s.remap_xyz(Tree::x(), Tree::y(), Tree::z());
552 let mut ctx = Context::new();
553 let v_ = ctx.import(&v);
554 assert_eq!(ctx.eval_xyz(v_, 0.0, 1.0, 0.0).unwrap(), 2.0);
555
556 let s = Tree::z() + 1.0;
558
559 let v = s.remap_xyz(Tree::y(), Tree::z(), Tree::x());
560 let mut ctx = Context::new();
561 let v_ = ctx.import(&v);
562 assert_eq!(ctx.eval_xyz(v_, 1.0, 0.0, 0.0).unwrap(), 2.0);
563
564 let v = s.remap_xyz(Tree::z(), Tree::x(), Tree::y());
565 let mut ctx = Context::new();
566 let v_ = ctx.import(&v);
567 assert_eq!(ctx.eval_xyz(v_, 0.0, 1.0, 0.0).unwrap(), 2.0);
568
569 let v = s.remap_xyz(Tree::x(), Tree::y(), Tree::z());
570 let mut ctx = Context::new();
571 let v_ = ctx.import(&v);
572 assert_eq!(ctx.eval_xyz(v_, 0.0, 0.0, 1.0).unwrap(), 2.0);
573
574 let s = Tree::x() + 1.0;
576 let one = Tree::constant(3.0);
577 let v = s.remap_xyz(one, Tree::y(), Tree::z());
578 let v_ = ctx.import(&v);
579 assert_eq!(ctx.eval_xyz(v_, 0.0, 1.0, 0.0).unwrap(), 4.0);
580 }
581
582 #[test]
583 fn test_remap_affine() {
584 let s = Tree::x();
585 let t = nalgebra::convert(nalgebra::Rotation3::<f32>::from_axis_angle(
587 &nalgebra::Vector3::<f32>::z_axis(),
588 -std::f32::consts::FRAC_PI_4,
589 ));
590 let s = s.remap_affine(t);
591 let s = s.remap_affine(t);
592
593 let TreeOp::RemapAffine { target, .. } = &*s else {
594 panic!("invalid shape");
595 };
596 assert!(matches!(&**target, TreeOp::Input(Var::X)));
597
598 let mut ctx = Context::new();
599 let v_ = ctx.import(&s);
600
601 assert!((ctx.eval_xyz(v_, 0.0, 1.0, 0.0).unwrap() - 1.0).abs() < 1e-6);
602 assert!(
603 (ctx.eval_xyz(v_, 0.0, -2.0, 0.0).unwrap() - -2.0).abs() < 1e-6
604 );
605 }
606
607 #[test]
608 fn test_remap_order() {
609 let translate = nalgebra::convert(nalgebra::Translation3::<f32>::new(
610 3.0, 0.0, 0.0,
611 ));
612 let scale =
613 nalgebra::convert(nalgebra::Scale3::<f32>::new(0.5, 0.5, 0.5));
614
615 let s = Tree::x();
616 let s = s.remap_affine(translate);
617 let s = s.remap_affine(scale);
618
619 let TreeOp::RemapAffine { target, .. } = &*s else {
621 panic!("invalid shape");
622 };
623 assert!(matches!(&**target, TreeOp::Input(Var::X)));
624
625 let mut ctx = Context::new();
627 let v_ = ctx.import(&s);
628 assert_eq!(ctx.eval_xyz(v_, 1.0, 0.0, 0.0).unwrap(), 3.5);
629 assert_eq!(ctx.eval_xyz(v_, 2.0, 0.0, 0.0).unwrap(), 4.0);
630
631 let manual = TreeOp::RemapAffine {
633 target: Arc::new(TreeOp::RemapAffine {
634 target: TreeOp::Input(Var::X).into(),
635 mat: scale,
636 }),
637 mat: translate,
638 }
639 .into();
640 let mut ctx = Context::new();
641 let v_ = ctx.import(&manual);
642 assert_eq!(ctx.eval_xyz(v_, 1.0, 0.0, 0.0).unwrap(), 3.5);
643 assert_eq!(ctx.eval_xyz(v_, 2.0, 0.0, 0.0).unwrap(), 4.0);
644
645 let s = Tree::x();
647 let s = s.remap_affine(scale);
648 let s = s.remap_affine(translate);
649
650 let mut ctx = Context::new();
651 let v_ = ctx.import(&s);
652 assert_eq!(ctx.eval_xyz(v_, 1.0, 0.0, 0.0).unwrap(), 2.0);
653 assert_eq!(ctx.eval_xyz(v_, 2.0, 0.0, 0.0).unwrap(), 2.5);
654 }
655
656 #[test]
657 fn deep_recursion_drop() {
658 let mut x = Tree::x();
659 for _ in 0..1_000_000 {
660 x += 1.0;
661 }
662 drop(x);
663 }
665
666 #[test]
667 fn deep_recursion_eq() {
668 let mut x1 = Tree::x();
669 for _ in 0..1_000_000 {
670 x1 += 1.0;
671 }
672 let mut x2 = Tree::x();
673 for _ in 0..1_000_000 {
674 x2 += 1.0;
675 }
676 assert_eq!(x1, x2);
677 }
678
679 #[test]
680 fn deep_recursion_hash() {
681 let mut x1 = Tree::x();
682 for _ in 0..1_000_000 {
683 x1 += 1.0;
684 }
685 let mut x2 = Tree::x();
686 for _ in 0..1_000_000 {
687 x2 += 1.0;
688 }
689 let state = std::hash::RandomState::new();
690 assert_eq!(state.hash_one(&x1), state.hash_one(&x2));
691 }
692
693 #[test]
694 fn deep_recursion_import() {
695 let mut x = Tree::x();
696 for _ in 0..1_000_000 {
697 x += 1.0;
698 }
699 let mut ctx = Context::new();
700 ctx.import(&x);
701 }
703
704 #[test]
705 fn tree_remap_multi() {
706 let mut ctx = Context::new();
707
708 let out = Tree::x() + Tree::y() + Tree::z();
709 let out =
710 out.remap_xyz(Tree::x() * 2.0, Tree::y() * 3.0, Tree::z() * 5.0);
711
712 let v_ = ctx.import(&out);
713 assert_eq!(ctx.eval_xyz(v_, 1.0, 1.0, 1.0).unwrap(), 10.0);
714 assert_eq!(ctx.eval_xyz(v_, 2.0, 1.0, 1.0).unwrap(), 12.0);
715 assert_eq!(ctx.eval_xyz(v_, 2.0, 2.0, 1.0).unwrap(), 15.0);
716 assert_eq!(ctx.eval_xyz(v_, 2.0, 2.0, 2.0).unwrap(), 20.0);
717
718 let out = out.remap_xyz(Tree::y(), Tree::z(), Tree::x());
719 let v_ = ctx.import(&out);
720 assert_eq!(ctx.eval_xyz(v_, 1.0, 1.0, 1.0).unwrap(), 10.0);
721 assert_eq!(ctx.eval_xyz(v_, 2.0, 1.0, 1.0).unwrap(), 15.0);
722 assert_eq!(ctx.eval_xyz(v_, 2.0, 2.0, 1.0).unwrap(), 17.0);
723 assert_eq!(ctx.eval_xyz(v_, 2.0, 2.0, 2.0).unwrap(), 20.0);
724 }
725
726 #[test]
727 fn tree_import_cache() {
728 let mut x = Tree::x();
729 for _ in 0..100_000 {
730 x += 1.0;
731 }
732 let mut ctx = Context::new();
733 let start = std::time::Instant::now();
734 ctx.import(&x);
735 let small = start.elapsed();
736
737 let x = x.clone() * x.clone() * x.clone() * x;
739 let mut ctx = Context::new();
740 let start = std::time::Instant::now();
741 ctx.import(&x);
742 let large = start.elapsed();
743
744 assert!(
745 large.as_millis() < small.as_millis() * 2,
746 "tree import cache failed: {large:?} is much larger than {small:?}"
747 );
748 }
749
750 #[test]
751 fn tree_import_nocache() {
752 let mut x = Tree::x();
753 for _ in 0..100_000 {
754 x += 1.0;
755 }
756 let mut ctx = Context::new();
757 let start = std::time::Instant::now();
758 ctx.import(&x);
759 let small = start.elapsed();
760
761 let x = x.remap_xyz(Tree::y(), Tree::z(), Tree::x())
763 * x.remap_xyz(Tree::z(), Tree::x(), Tree::y())
764 * x.remap_xyz(Tree::y(), Tree::x(), Tree::z())
765 * x;
766 let mut ctx = Context::new();
767 let start = std::time::Instant::now();
768 ctx.import(&x);
769 let large = start.elapsed();
770
771 assert!(
772 large.as_millis() > small.as_millis() * 2,
773 "tree import cache failed:
774 {large:?} is not much larger than {small:?}"
775 );
776 }
777
778 #[test]
779 fn tree_from_int() {
780 let a = Tree::from(3);
781 let b = a * 5;
782
783 let mut ctx = Context::new();
784 let root = ctx.import(&b);
785 assert_eq!(ctx.get_const(root).unwrap(), 15.0);
786 }
787
788 #[test]
789 fn tree_deriv() {
790 let x = Tree::x();
792 let vx = x.var().unwrap();
793 let d = x.deriv(vx);
794 let TreeOp::Const(v) = *d else {
795 panic!("invalid deriv {d:?}")
796 };
797 assert_eq!(v, 1.0);
798
799 let d = x.deriv(Var::new());
801 let TreeOp::Const(v) = *d else {
802 panic!("invalid deriv {d:?}")
803 };
804 assert_eq!(v, 0.0);
805 }
806
807 #[test]
808 fn tree_pow() {
809 let a = Tree::from(3);
810 let b = a.pow(3);
811 let c = a.pow(-3);
812 let d = a.pow(0);
813
814 let mut ctx = Context::new();
815 let root = ctx.import(&b);
816 assert_eq!(ctx.get_const(root).unwrap(), 27.0);
817 ctx.clear();
818 let root = ctx.import(&c);
819 assert_eq!(ctx.get_const(root).unwrap(), (1f32 / 3.0).powi(3));
820 ctx.clear();
821 let root = ctx.import(&d);
822 assert_eq!(ctx.get_const(root).unwrap(), 1.0);
823 }
824
825 #[test]
826 fn tree_poke() {
827 #[derive(facet::Facet)]
828 struct Transform {
829 tree: Tree,
830 x: f32,
831 }
832
833 let builder = facet::Partial::alloc::<Transform>()
834 .unwrap()
835 .set_field("tree", Tree::x() + 2.0 * Tree::y())
836 .unwrap()
837 .set_field("x", 1.0f32)
838 .unwrap();
839 let t: Transform = builder.build().unwrap().materialize().unwrap();
840 assert_eq!(t.x, 1.0);
841 let mut ctx = Context::new();
842 let node = ctx.import(&t.tree);
843 assert_eq!(ctx.eval_xyz(node, 1.0, 2.0, 3.0).unwrap(), 5.0);
844 }
845
846 #[test]
847 fn tree_eq_simple() {
848 let a = Tree::from(1.0) + Tree::x();
849 let a_ = a.clone();
850 let b = Tree::from(1.0) + Tree::x();
851 assert_eq!(a_, a);
852 assert_eq!(a, b);
853
854 let c = Tree::from(1.0) + Tree::y();
855 assert_ne!(a, c);
856 assert_ne!(b, c);
857
858 let a = Tree::x().cos();
859 let a_ = Tree::x().cos();
860 let b = Tree::x().sin();
861 assert_eq!(a, a_);
862 assert_ne!(a, b);
863 }
864
865 #[test]
866 fn tree_hash_simple() {
867 let a = Tree::from(1.0) + Tree::x();
868 let a_ = a.clone();
869 let b = Tree::from(1.0) + Tree::x();
870 let state = std::hash::RandomState::new();
871
872 assert_eq!(state.hash_one(&a), state.hash_one(&b));
873 assert_eq!(state.hash_one(&a), state.hash_one(&a_));
874
875 let c = Tree::from(1.0) + Tree::y();
876 assert_ne!(state.hash_one(&a), state.hash_one(&c));
877
878 let d = Tree::from(1.0) * Tree::x();
879 assert_ne!(state.hash_one(&a), state.hash_one(&d));
880
881 let a = Tree::x().cos();
882 let a_ = Tree::x().cos();
883 let b = Tree::x().sin();
884 assert_eq!(state.hash_one(&a), state.hash_one(&a_));
885 assert_ne!(state.hash_one(&a), state.hash_one(&b));
886 }
887
888 #[test]
889 fn tree_eq_nan() {
890 let a = Tree::from(f32::NAN);
891 let a_ = a.clone();
892 let b = Tree::from(f32::NAN);
893
894 assert!(a.ptr_eq(&a_));
895 assert_eq!(a, a_);
896 assert!(!a.ptr_eq(&b));
897 assert_eq!(a, b);
898
899 let affine: nalgebra::Affine3<_> =
900 nalgebra::convert(nalgebra::Translation3::new(1.0, 2.0, f32::NAN));
901 let a = Tree::x().remap_affine(affine);
902 let b = Tree::x().remap_affine(affine);
903 assert_eq!(a, b);
904 }
905
906 #[test]
907 fn tree_hash_nan() {
908 let a = Tree::from(f32::NAN);
909 let a_ = a.clone();
910 let b = Tree::from(f32::NAN);
911 let c = Tree::from(1.0);
912 let state = std::hash::RandomState::new();
913
914 assert_eq!(state.hash_one(&a), state.hash_one(a_));
915 assert_eq!(state.hash_one(&a), state.hash_one(&b));
916 assert_ne!(state.hash_one(&a), state.hash_one(&c));
917
918 let affine: nalgebra::Affine3<_> =
919 nalgebra::convert(nalgebra::Translation3::new(1.0, 2.0, f32::NAN));
920 let a = Tree::x().remap_affine(affine);
921 let b = Tree::x().remap_affine(affine);
922 assert_eq!(state.hash_one(&a), state.hash_one(&b));
923
924 let affine: nalgebra::Affine3<_> =
925 nalgebra::convert(nalgebra::Translation3::new(1.0, 2.0, 3.0));
926 let c = Tree::x().remap_affine(affine);
927 assert_ne!(state.hash_one(&a), state.hash_one(&c));
928 }
929}