1use crate::{Sphere, VdCorput};
17use std::f64::consts::PI;
18use std::sync::{Arc, LazyLock, Mutex};
19
20fn linspace(start: f64, stop: f64, num: usize) -> Vec<f64> {
22 if num == 1 {
23 return vec![start];
24 }
25 let step = (stop - start) / (num as f64 - 1.0);
26 (0..num).map(|i| start + i as f64 * step).collect()
27}
28
29fn simple_interp(x: f64, xp: &[f64], yp: &[f64]) -> f64 {
31 if x <= xp[0] {
32 return yp[0];
33 }
34 if x >= xp[xp.len() - 1] {
35 return yp[yp.len() - 1];
36 }
37
38 let i = match xp.binary_search_by(|&v| v.partial_cmp(&x).unwrap()) {
40 Ok(i) => i,
41 Err(i) => i - 1,
42 };
43
44 let t = (x - xp[i]) / (xp[i + 1] - xp[i]);
46 yp[i] + t * (yp[i + 1] - yp[i])
47}
48
49struct SphereTables {
51 x: Vec<f64>,
52 neg_cosine: Vec<f64>,
53 sine: Vec<f64>,
54 f2: Vec<f64>,
55 half_pi: f64,
56}
57
58impl SphereTables {
59 fn new() -> Self {
60 let x = linspace(0.0, PI, 300);
61 let neg_cosine = x.iter().map(|&x| -x.cos()).collect();
62 let sine = x.iter().map(|&x| x.sin()).collect();
63 let f2 = x
64 .iter()
65 .zip(&neg_cosine)
66 .zip(&sine)
67 .map(|((&x, &nc), &s)| (x + nc * s) / 2.0)
68 .collect();
69 let half_pi = PI / 2.0;
70
71 Self {
72 x,
73 neg_cosine,
74 sine,
75 f2,
76 half_pi,
77 }
78 }
79
80 fn get(&self) -> (&[f64], &[f64], &[f64], &[f64], f64) {
81 (
82 &self.x,
83 &self.neg_cosine,
84 &self.sine,
85 &self.f2,
86 self.half_pi,
87 )
88 }
89}
90
91static SPHERE_TABLES: LazyLock<SphereTables> = LazyLock::new(SphereTables::new);
93
94const TP_CACHE_MAX: usize = 64;
95
96fn tp_cache_get(n: usize) -> Arc<[f64]> {
98 static TP_CACHE: LazyLock<Mutex<Vec<Arc<[f64]>>>> = LazyLock::new(|| Mutex::new(Vec::new()));
99
100 let mut cache = TP_CACHE.lock().unwrap();
101
102 while cache.len() <= n && cache.len() < TP_CACHE_MAX {
103 let new_n = cache.len();
104 let x = &SPHERE_TABLES.x;
105 let neg_cosine = &SPHERE_TABLES.neg_cosine;
106 let sine = &SPHERE_TABLES.sine;
107
108 let tp: Arc<[f64]> = if new_n == 0 {
109 Arc::from(x.clone().into_boxed_slice())
110 } else if new_n == 1 {
111 Arc::from(neg_cosine.clone().into_boxed_slice())
112 } else {
113 let tp_minus2 = &cache[new_n - 2];
114 let v: Vec<f64> = x
115 .iter()
116 .enumerate()
117 .map(|(i, _xi)| {
118 ((new_n - 1) as f64 * tp_minus2[i]
119 + neg_cosine[i] * sine[i].powi((new_n - 1) as i32))
120 / new_n as f64
121 })
122 .collect();
123 Arc::from(v.into_boxed_slice())
124 };
125 cache.push(tp);
126 }
127
128 if n < cache.len() {
129 cache[n].clone()
130 } else {
131 compute_tp_arc(n)
132 }
133}
134
135fn compute_tp_arc(n: usize) -> Arc<[f64]> {
140 if n == 0 {
141 return Arc::from(SPHERE_TABLES.x.clone().into_boxed_slice());
142 }
143 if n == 1 {
144 return Arc::from(SPHERE_TABLES.neg_cosine.clone().into_boxed_slice());
145 }
146
147 let x = &SPHERE_TABLES.x;
148 let neg_cosine = &SPHERE_TABLES.neg_cosine;
149 let sine = &SPHERE_TABLES.sine;
150
151 let even = n.is_multiple_of(2);
152 let mut prev: Vec<f64> = if even {
153 x.to_vec()
154 } else {
155 neg_cosine.to_vec()
156 };
157 let start: usize = if even { 2 } else { 3 };
158
159 for k in (start..=n).step_by(2) {
160 let tp_k: Vec<f64> = x
161 .iter()
162 .enumerate()
163 .map(|(i, _xi)| {
164 ((k - 1) as f64 * prev[i] + neg_cosine[i] * sine[i].powi((k - 1) as i32)) / k as f64
165 })
166 .collect();
167 if k == n {
168 return Arc::from(tp_k.into_boxed_slice());
169 }
170 prev = tp_k;
171 }
172 unreachable!()
173}
174
175pub trait SphereGen: Send + Sync {
180 fn pop(&mut self) -> Vec<f64>;
185
186 fn reseed(&mut self, seed: u64);
192}
193
194impl SphereGen for Sphere {
195 fn pop(&mut self) -> Vec<f64> {
196 Sphere::pop(self).to_vec()
197 }
198
199 fn reseed(&mut self, seed: u64) {
200 Sphere::reseed(self, seed);
201 }
202}
203
204pub struct Sphere3 {
216 vdc: VdCorput,
217 sphere2: Sphere,
218}
219
220impl Sphere3 {
221 pub fn new(base: &[u64]) -> Self {
227 assert!(base.len() >= 3, "Sphere3 requires at least 3 bases");
228 Self {
229 vdc: VdCorput::new(base[0]),
230 sphere2: Sphere::new([base[1], base[2]]),
231 }
232 }
233
234 pub fn get_index(&self) -> u64 {
236 self.vdc.get_index()
237 }
238
239 pub fn advance(&self, n: u64) {
245 self.vdc.advance(n);
246 self.sphere2.advance(n);
247 }
248}
249
250impl SphereGen for Sphere3 {
251 fn pop(&mut self) -> Vec<f64> {
258 let tables = SPHERE_TABLES.get();
259 let ti = tables.4 * self.vdc.pop();
260 let xi = simple_interp(ti, tables.3, tables.0);
261 let cosxi = xi.cos();
262 let sinxi = xi.sin();
263
264 let sphere2_point = self.sphere2.pop();
265 let mut result = Vec::with_capacity(4);
266 for &s in &sphere2_point {
267 result.push(sinxi * s);
268 }
269 result.push(cosxi);
270 result
271 }
272
273 fn reseed(&mut self, seed: u64) {
274 self.vdc.reseed(seed);
275 self.sphere2.reseed(seed);
276 }
277}
278
279pub struct SphereN {
291 vdc: VdCorput,
292 s_gen: Box<dyn SphereGen>,
293 n: usize,
294 tp: Arc<[f64]>,
295 tp_start: f64,
296 range: f64,
297}
298
299impl SphereN {
300 pub fn new(base: &[u64]) -> Self {
307 let n = base.len() - 1;
308 assert!(n >= 2, "SphereN requires at least 3 bases (n >= 2)");
309
310 let vdc = VdCorput::new(base[0]);
311
312 let s_gen: Box<dyn SphereGen> = if n == 2 {
313 Box::new(Sphere::new([base[1], base[2]]))
314 } else {
315 Box::new(SphereN::new(&base[1..]))
316 };
317
318 let tp = tp_cache_get(n);
319 let tp_start = tp[0];
320 let range = tp[tp.len() - 1] - tp_start;
321
322 Self {
323 vdc,
324 s_gen,
325 n,
326 tp,
327 tp_start,
328 range,
329 }
330 }
331}
332
333impl SphereGen for SphereN {
334 fn pop(&mut self) -> Vec<f64> {
340 if self.n == 2 {
341 let tables = SPHERE_TABLES.get();
342 let ti = tables.4 * self.vdc.pop(); let xi = simple_interp(ti, tables.3, tables.0);
344 let cosxi = xi.cos();
345 let sinxi = xi.sin();
346
347 let sphere_point = self.s_gen.pop();
348 let mut result = Vec::with_capacity(sphere_point.len() + 1);
349 for &s in &sphere_point {
350 result.push(sinxi * s);
351 }
352 result.push(cosxi);
353 return result;
354 }
355
356 let vd = self.vdc.pop();
357 let ti = self.tp_start + self.range * vd; let xi = simple_interp(ti, &self.tp, &SPHERE_TABLES.x);
359 let sinphi = xi.sin();
360
361 let sphere_point = self.s_gen.pop();
362 let mut result = Vec::with_capacity(sphere_point.len() + 1);
363 for &s in &sphere_point {
364 result.push(s * sinphi);
365 }
366 result.push(xi.cos());
367 result
368 }
369
370 fn reseed(&mut self, seed: u64) {
371 self.vdc.reseed(seed);
372 self.s_gen.reseed(seed);
373 }
374}
375
376impl SphereN {
377 pub fn get_index(&self) -> u64 {
379 self.vdc.get_index()
380 }
381
382 pub fn advance(&self, n: u64) {
388 self.vdc.advance(n);
389 }
390}
391
392#[cfg(test)]
393mod tests {
394 use super::*;
395 use approx::assert_relative_eq;
396
397 #[test]
398 fn test_linspace() {
399 let result = linspace(0.0, 1.0, 5);
400 let expected = [0.0, 0.25, 0.5, 0.75, 1.0];
401 assert_eq!(result.len(), 5);
402 for i in 0..5 {
403 assert_relative_eq!(result[i], expected[i], epsilon = 1e-10);
404 }
405
406 let result = linspace(0.0, 1.0, 1);
407 assert_eq!(result, vec![0.0]);
408
409 let result = linspace(-1.0, 1.0, 3);
410 let expected = [-1.0, 0.0, 1.0];
411 for i in 0..3 {
412 assert_relative_eq!(result[i], expected[i], epsilon = 1e-10);
413 }
414 }
415
416 #[test]
417 fn test_simple_interp() {
418 let xp = vec![0.0, 1.0, 2.0, 3.0];
419 let yp = vec![0.0, 2.0, 4.0, 6.0]; let result = simple_interp(0.5, &xp, &yp);
422 assert_relative_eq!(result, 1.0, epsilon = 1e-10);
423
424 let result = simple_interp(1.5, &xp, &yp);
425 assert_relative_eq!(result, 3.0, epsilon = 1e-10);
426
427 let result = simple_interp(-0.5, &xp, &yp);
428 assert_relative_eq!(result, 0.0, epsilon = 1e-10);
429
430 let result = simple_interp(3.5, &xp, &yp);
431 assert_relative_eq!(result, 6.0, epsilon = 1e-10);
432
433 let result = simple_interp(2.0, &xp, &yp);
434 assert_relative_eq!(result, 4.0, epsilon = 1e-10);
435 }
436
437 #[test]
438 fn test_get_tp() {
439 let tp0 = tp_cache_get(0);
440 assert_eq!(tp0.len(), 300);
441 assert_relative_eq!(tp0[0], 0.0, epsilon = 1e-10);
442 assert_relative_eq!(tp0[tp0.len() - 1], PI, epsilon = 1e-10);
443
444 let tp1 = tp_cache_get(1);
445 assert_eq!(tp1.len(), 300);
446 assert_relative_eq!(tp1[0], -0.0f64.cos(), epsilon = 1e-10);
447 assert_relative_eq!(tp1[tp1.len() - 1], -PI.cos(), epsilon = 1e-10);
448
449 let tp2 = tp_cache_get(2);
450 assert_eq!(tp2.len(), 300);
451 }
452
453 #[test]
454 fn test_sphere3_basic() {
455 let mut sgen = Sphere3::new(&[2, 3, 5]);
456 sgen.reseed(0);
457
458 let point = sgen.pop();
459 assert_eq!(point.len(), 4);
460
461 let radius_sq = point.iter().map(|&x| x * x).sum::<f64>();
462 assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
463
464 for &coord in &point {
465 assert!((-1.0..=1.0).contains(&coord));
466 }
467 }
468
469 #[test]
470 fn test_sphere3_consistency() {
471 let bases = vec![vec![2, 3, 5], vec![2, 5, 3], vec![3, 2, 7]];
472
473 for base in bases {
474 let mut sgen = Sphere3::new(&base);
475 sgen.reseed(0);
476
477 let points: Vec<_> = (0..5).map(|_| sgen.pop()).collect();
478
479 for point in points.iter() {
480 let radius_sq = point.iter().map(|&x| x * x).sum::<f64>();
481 assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
482
483 for &coord in point {
484 assert!((-1.0..=1.0).contains(&coord));
485 }
486 }
487 }
488 }
489
490 #[test]
491 fn test_sphere3_reseed() {
492 let mut sgen = Sphere3::new(&[2, 3, 5]);
493
494 sgen.reseed(0);
495 let seq1: Vec<_> = (0..3).map(|_| sgen.pop()).collect();
496
497 sgen.reseed(0);
498 let seq2: Vec<_> = (0..3).map(|_| sgen.pop()).collect();
499
500 for i in 0..3 {
501 for j in 0..4 {
502 assert_relative_eq!(seq1[i][j], seq2[i][j], epsilon = 1e-10);
503 }
504 }
505
506 sgen.reseed(1);
507 let seq3: Vec<_> = (0..3).map(|_| sgen.pop()).collect();
508
509 let mut different = false;
510 for i in 0..3 {
511 for j in 0..4 {
512 if (seq1[i][j] - seq3[i][j]).abs() > 1e-10 {
513 different = true;
514 break;
515 }
516 }
517 if different {
518 break;
519 }
520 }
521 assert!(
522 different,
523 "Sequences with different seeds should be different"
524 );
525 }
526
527 #[test]
528 fn test_spheren_basic() {
529 let mut sgen = SphereN::new(&[2, 3, 5, 7]);
530 sgen.reseed(0);
531
532 let point = sgen.pop();
533 assert_eq!(point.len(), 5);
534
535 let radius_sq = point.iter().map(|&x| x * x).sum::<f64>();
536 assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
537 }
538
539 #[test]
540 fn test_spheren_higher_dimensions() {
541 let mut sgen = SphereN::new(&[2, 3, 5, 7, 11]);
542 sgen.reseed(0);
543
544 let point = sgen.pop();
545 assert_eq!(point.len(), 6);
546
547 let radius_sq = point.iter().map(|&x| x * x).sum::<f64>();
548 assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
549 }
550
551 #[test]
552 fn test_spheren_reseed() {
553 let mut sgen = SphereN::new(&[2, 3, 5, 7]);
554
555 sgen.reseed(0);
556 let seq1: Vec<_> = (0..3).map(|_| sgen.pop()).collect();
557
558 sgen.reseed(0);
559 let seq2: Vec<_> = (0..3).map(|_| sgen.pop()).collect();
560
561 for i in 0..3 {
562 for j in 0..5 {
563 assert_relative_eq!(seq1[i][j], seq2[i][j], epsilon = 1e-10);
564 }
565 }
566 }
567
568 #[test]
569 fn test_comparison_with_python() {
570 let expected_sphere3 = [
572 0.2913440162992141,
573 0.8966646826186098,
574 -0.33333333333333337,
575 6.123233995736766e-17,
576 ];
577
578 let expected_spheren = [
579 0.4809684718990214,
580 0.6031153874276115,
581 -0.5785601510223212,
582 0.2649326520763179,
583 6.123233995736766e-17,
584 ];
585
586 let mut sgen3 = Sphere3::new(&[2, 3, 5]);
587 sgen3.reseed(0);
588 let result3 = sgen3.pop();
589
590 for i in 0..4 {
591 assert_relative_eq!(result3[i], expected_sphere3[i], epsilon = 1e-10);
592 }
593
594 let mut sgen_n = SphereN::new(&[2, 3, 5, 7]);
595 sgen_n.reseed(0);
596 let result_n = sgen_n.pop();
597
598 for i in 0..5 {
599 assert_relative_eq!(result_n[i], expected_spheren[i], epsilon = 1e-10);
600 }
601 }
602
603 #[test]
604 #[should_panic(expected = "Sphere3 requires at least 3 bases")]
605 fn test_sphere3_insufficient_bases() {
606 Sphere3::new(&[2, 3]);
607 }
608
609 #[test]
610 #[should_panic(expected = "SphereN requires at least 3 bases")]
611 fn test_spheren_insufficient_bases() {
612 SphereN::new(&[2, 3]);
613 }
614
615 #[test]
616 fn test_sphere_tables_thread_safety() {
617 use std::sync::{Arc, Barrier};
618 use std::thread;
619
620 let num_threads = 8;
621 let barrier = Arc::new(Barrier::new(num_threads));
622 let mut handles = vec![];
623
624 for _ in 0..num_threads {
625 let barrier_clone = Arc::clone(&barrier);
626 let handle = thread::spawn(move || {
627 barrier_clone.wait();
629
630 let tables = SPHERE_TABLES.get();
632 assert_eq!(tables.0.len(), 300); assert_eq!(tables.1.len(), 300); assert_eq!(tables.2.len(), 300); assert_eq!(tables.3.len(), 300); assert_eq!(tables.4, std::f64::consts::PI / 2.0); });
638 handles.push(handle);
639 }
640
641 for handle in handles {
642 handle.join().unwrap();
643 }
644 }
645
646 #[test]
647 fn test_get_tp_cache_thread_safety() {
648 use std::sync::{Arc, Barrier};
649 use std::thread;
650
651 let num_threads = 8;
652 let barrier = Arc::new(Barrier::new(num_threads));
653 let mut handles = vec![];
654
655 for thread_id in 0..num_threads {
656 let barrier_clone = Arc::clone(&barrier);
657 let handle = thread::spawn(move || {
658 barrier_clone.wait();
659
660 let n = thread_id % 5; let tp = tp_cache_get(n);
663
664 assert_eq!(tp.len(), 300);
666
667 if n == 0 {
671 assert!(tp[0] >= 0.0 && tp[0] <= std::f64::consts::PI);
672 assert!(tp[tp.len() - 1] >= 0.0 && tp[tp.len() - 1] <= std::f64::consts::PI);
673 } else if n == 1 {
674 assert!(tp[0] >= -1.0 && tp[0] <= 1.0);
675 assert!(tp[tp.len() - 1] >= -1.0 && tp[tp.len() - 1] <= 1.0);
676 }
677 for &val in &*tp {
679 assert!(val.is_finite());
680 }
681 });
682 handles.push(handle);
683 }
684
685 for handle in handles {
686 handle.join().unwrap();
687 }
688 }
689
690 #[test]
691 fn test_sphere3_concurrent_access() {
692 use std::sync::{Arc, Mutex};
693 use std::thread;
694
695 let sgen = Arc::new(Mutex::new(Sphere3::new(&[2, 3, 5])));
696 sgen.lock().unwrap().reseed(0);
697
698 let mut handles = vec![];
699 let results = Arc::new(Mutex::new(Vec::new()));
700
701 for _ in 0..4 {
702 let sgen_clone = Arc::clone(&sgen);
703 let results_clone = Arc::clone(&results);
704
705 let handle = thread::spawn(move || {
706 let mut local_points = Vec::new();
707 for _ in 0..5 {
708 let mut generator = sgen_clone.lock().unwrap();
709 let point = generator.pop();
710 local_points.push(point);
711 }
712 let mut results = results_clone.lock().unwrap();
713 results.push(local_points);
714 });
715
716 handles.push(handle);
717 }
718
719 for handle in handles {
720 handle.join().unwrap();
721 }
722
723 let results = results.lock().unwrap();
724 assert_eq!(results.len(), 4);
725
726 for thread_results in results.iter() {
727 assert_eq!(thread_results.len(), 5);
728 for point in thread_results {
729 assert_eq!(point.len(), 4);
730 let radius_sq = point.iter().map(|&x| x * x).sum::<f64>();
731 assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
732 }
733 }
734 }
735
736 #[test]
737 fn test_spheren_concurrent_access() {
738 use std::sync::{Arc, Mutex};
739 use std::thread;
740
741 let sgen = Arc::new(Mutex::new(SphereN::new(&[2, 3, 5, 7, 11])));
742 sgen.lock().unwrap().reseed(0);
743
744 let mut handles = vec![];
745 let results = Arc::new(Mutex::new(Vec::new()));
746
747 for _ in 0..4 {
748 let sgen_clone = Arc::clone(&sgen);
749 let results_clone = Arc::clone(&results);
750
751 let handle = thread::spawn(move || {
752 let mut local_points = Vec::new();
753 for _ in 0..3 {
754 let mut generator = sgen_clone.lock().unwrap();
755 let point = generator.pop();
756 local_points.push(point);
757 }
758 let mut results = results_clone.lock().unwrap();
759 results.push(local_points);
760 });
761
762 handles.push(handle);
763 }
764
765 for handle in handles {
766 handle.join().unwrap();
767 }
768
769 let results = results.lock().unwrap();
770 assert_eq!(results.len(), 4);
771
772 for thread_results in results.iter() {
773 assert_eq!(thread_results.len(), 3);
774 for point in thread_results {
775 assert_eq!(point.len(), 6);
776 let radius_sq = point.iter().map(|&x| x * x).sum::<f64>();
777 assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
778 }
779 }
780 }
781
782 #[test]
783 fn test_multiple_sphere_instances_concurrent() {
784 use std::sync::{Arc, Barrier};
785 use std::thread;
786
787 let num_threads = 6;
788 let barrier = Arc::new(Barrier::new(num_threads));
789 let mut handles = vec![];
790
791 for thread_id in 0..num_threads {
792 let barrier_clone = Arc::clone(&barrier);
793 let handle = thread::spawn(move || {
794 barrier_clone.wait();
795
796 let bases = match thread_id % 3 {
798 0 => &[2, 3, 5][..],
799 1 => &[3, 5, 7][..],
800 _ => &[5, 7, 11][..],
801 };
802
803 let mut sgen: Box<dyn SphereGen> = if thread_id < 3 {
804 Box::new(Sphere3::new(bases))
805 } else {
806 Box::new(SphereN::new(&[bases[0], bases[1], bases[2], 13]))
807 };
808
809 sgen.reseed(thread_id as u64);
810
811 for _ in 0..5 {
813 let point = sgen.pop();
814 let radius_sq = point.iter().map(|&x| x * x).sum::<f64>();
815 assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
816 }
817 });
818
819 handles.push(handle);
820 }
821
822 for handle in handles {
823 handle.join().unwrap();
824 }
825 }
826
827 #[test]
828 fn test_sphere_trait_send_sync() {
829 fn is_send_sync<T: Send + Sync>() {}
831
832 is_send_sync::<Sphere3>();
833 is_send_sync::<SphereN>();
834
835 let mut sgen3: Box<dyn SphereGen> = Box::new(Sphere3::new(&[2, 3, 5]));
837 let mut sgen_n: Box<dyn SphereGen> = Box::new(SphereN::new(&[2, 3, 5, 7]));
838
839 sgen3.reseed(0);
841 sgen_n.reseed(0);
842
843 let _point3 = sgen3.pop();
844 let _point_n = sgen_n.pop();
845 }
846
847 #[test]
850 fn test_linspace_edge_cases() {
851 let result = linspace(1.0, 1.0, 5);
853 assert_eq!(result, vec![1.0, 1.0, 1.0, 1.0, 1.0]);
854
855 let result = linspace(-1.0, -0.5, 3);
857 let expected = [-1.0, -0.75, -0.5];
858 for i in 0..3 {
859 assert_relative_eq!(result[i], expected[i], epsilon = 1e-10);
860 }
861
862 let result = linspace(0.0, 1.0, 1000);
864 assert_eq!(result.len(), 1000);
865 assert_relative_eq!(result[0], 0.0, epsilon = 1e-10);
866 assert_relative_eq!(result[999], 1.0, epsilon = 1e-10);
867 assert_relative_eq!(result[500], 0.5005005005005005, epsilon = 1e-10);
868 }
869
870 #[test]
871 fn test_simple_interp_edge_cases() {
872 let xp = vec![0.5];
874 let yp = vec![1.0];
875 let result = simple_interp(0.5, &xp, &yp);
876 assert_relative_eq!(result, 1.0, epsilon = 1e-10);
877
878 let xp = vec![0.0, 1.0, 2.0];
880 let yp = vec![5.0, 5.0, 5.0];
881 let result = simple_interp(1.5, &xp, &yp);
882 assert_relative_eq!(result, 5.0, epsilon = 1e-10);
883
884 let xp = vec![0.0, 0.1, 0.5, 2.0];
886 let yp = vec![0.0, 1.0, 2.0, 3.0];
887 let result = simple_interp(0.3, &xp, &yp);
888 let expected = 1.0 + (0.3 - 0.1) / (0.5 - 0.1) * (2.0 - 1.0);
890 assert_relative_eq!(result, expected, epsilon = 1e-10);
891 }
892
893 #[test]
894 fn test_sphere_n_higher_dimensions() {
895 let bases: Vec<u64> = (2..=12).collect();
897 let mut sgen = SphereN::new(&bases);
898 sgen.reseed(0);
899
900 let point = sgen.pop();
901 assert_eq!(point.len(), 12); let radius_sq = point.iter().map(|&x| x * x).sum::<f64>();
904 assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
905
906 let bases: Vec<u64> = (2..=22).collect();
908 let mut sgen = SphereN::new(&bases);
909 sgen.reseed(0);
910
911 let point = sgen.pop();
912 assert_eq!(point.len(), 22); let radius_sq = point.iter().map(|&x| x * x).sum::<f64>();
915 assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
916 }
917
918 #[test]
919 fn test_sphere_n_recursive_structure() {
920 let mut sgen4 = SphereN::new(&[2, 3, 5, 7]);
922 sgen4.reseed(0);
923
924 let point4 = sgen4.pop();
925 assert_eq!(point4.len(), 5);
926
927 let mut sgen5 = SphereN::new(&[2, 3, 5, 7, 11]);
929 sgen5.reseed(0);
930
931 let point5 = sgen5.pop();
932 assert_eq!(point5.len(), 6);
933
934 let radius_sq4 = point4.iter().map(|&x| x * x).sum::<f64>();
936 let radius_sq5 = point5.iter().map(|&x| x * x).sum::<f64>();
937 assert_relative_eq!(radius_sq4, 1.0, epsilon = 1e-10);
938 assert_relative_eq!(radius_sq5, 1.0, epsilon = 1e-10);
939 }
940
941 #[test]
942 fn test_sphere_tables_properties() {
943 let tables = SPHERE_TABLES.get();
944 let (x, neg_cosine, sine, f2, half_pi) = tables;
945
946 assert_eq!(x.len(), 300);
948 assert_eq!(neg_cosine.len(), 300);
949 assert_eq!(sine.len(), 300);
950 assert_eq!(f2.len(), 300);
951
952 assert_relative_eq!(half_pi, PI / 2.0, epsilon = 1e-10);
954
955 assert_relative_eq!(x[0], 0.0, epsilon = 1e-10);
957 assert_relative_eq!(x[299], PI, epsilon = 1e-10);
958
959 for i in 0..300 {
961 assert_relative_eq!(neg_cosine[i], -x[i].cos(), epsilon = 1e-10);
962 }
963
964 for i in 0..300 {
966 assert_relative_eq!(sine[i], x[i].sin(), epsilon = 1e-10);
967 }
968
969 for i in 0..300 {
971 let expected = (x[i] + neg_cosine[i] * sine[i]) / 2.0;
972 assert_relative_eq!(f2[i], expected, epsilon = 1e-10);
973 }
974 }
975
976 #[test]
977 fn test_get_tp_higher_dimensions() {
978 let tp5 = tp_cache_get(5);
980 assert_eq!(tp5.len(), 300);
981
982 let tp10 = tp_cache_get(10);
983 assert_eq!(tp10.len(), 300);
984
985 let tp20 = tp_cache_get(20);
986 assert_eq!(tp20.len(), 300);
987
988 for &val in &*tp5 {
990 assert!(val.is_finite());
991 }
992 for &val in &*tp10 {
993 assert!(val.is_finite());
994 }
995 for &val in &*tp20 {
996 assert!(val.is_finite());
997 }
998 }
999
1000 #[test]
1001 fn test_sphere_sequence_distribution() {
1002 let mut sgen = SphereN::new(&[2, 3, 5, 7, 11, 13]);
1004 sgen.reseed(0);
1005
1006 let mut points = Vec::new();
1007 for _ in 0..100 {
1008 points.push(sgen.pop());
1009 }
1010
1011 for point in &points {
1013 let radius_sq = point.iter().map(|&x| x * x).sum::<f64>();
1014 assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1015 }
1016
1017 for i in 1..points.len() {
1019 let mut same = true;
1020 for (j, &coord) in points[i].iter().enumerate() {
1021 if (coord - points[0][j]).abs() > 1e-10 {
1022 same = false;
1023 break;
1024 }
1025 }
1026 if i < 10 {
1027 assert!(!same, "First few points should be different");
1028 }
1029 }
1030 }
1031
1032 #[test]
1033 fn test_sphere_spheregen_impl() {
1034 let mut sgen: Box<dyn SphereGen> = Box::new(Sphere::new([2, 3]));
1035 sgen.reseed(0);
1036
1037 let point = sgen.pop();
1038 assert_eq!(point.len(), 3);
1039
1040 let radius_sq = point.iter().map(|&x| x * x).sum::<f64>();
1041 assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1042
1043 sgen.reseed(0);
1045 let point1 = sgen.pop();
1046
1047 sgen.reseed(0);
1048 let point2 = sgen.pop();
1049
1050 for i in 0..3 {
1051 assert_relative_eq!(point1[i], point2[i], epsilon = 1e-10);
1052 }
1053 }
1054
1055 #[test]
1056 fn test_sphere_n_different_bases() {
1057 let bases = vec![vec![3, 5, 7, 11], vec![5, 7, 11, 13], vec![7, 11, 13, 17]];
1059
1060 for base in bases {
1061 let mut sgen = SphereN::new(&base);
1062 sgen.reseed(0);
1063
1064 let point = sgen.pop();
1065 assert_eq!(point.len(), base.len() + 1); let radius_sq = point.iter().map(|&x| x * x).sum::<f64>();
1068 assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1069 }
1070 }
1071
1072 #[test]
1073 fn test_sphere3_different_bases() {
1074 let bases = vec![vec![3, 5, 7], vec![5, 7, 11], vec![7, 11, 13]];
1076
1077 for base in bases {
1078 let mut sgen = Sphere3::new(&base);
1079 sgen.reseed(0);
1080
1081 let point = sgen.pop();
1082 assert_eq!(point.len(), 4);
1083
1084 let radius_sq = point.iter().map(|&x| x * x).sum::<f64>();
1085 assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1086 }
1087 }
1088
1089 #[test]
1090 fn test_sphere_n_large_seed() {
1091 let mut sgen = SphereN::new(&[2, 3, 5, 7]);
1093
1094 for seed in [0, 100, 1000, 10000, 100000] {
1095 sgen.reseed(seed);
1096 let point = sgen.pop();
1097
1098 let radius_sq = point.iter().map(|&x| x * x).sum::<f64>();
1099 assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1100
1101 for coord in point {
1102 assert!(coord.is_finite());
1103 assert!((-1.0..=1.0).contains(&coord));
1104 }
1105 }
1106 }
1107
1108 #[test]
1109 fn test_sphere_coordinate_bounds() {
1110 let mut sgen = SphereN::new(&[2, 3, 5, 7, 11, 13, 17]);
1112 sgen.reseed(0);
1113
1114 for _ in 0..100 {
1115 let point = sgen.pop();
1116
1117 for &coord in &point {
1118 assert!((-1.0..=1.0).contains(&coord));
1119 assert!(coord.is_finite());
1120 }
1121 }
1122 }
1123
1124 #[test]
1125 fn test_sphere3_get_index() {
1126 let mut sgen = Sphere3::new(&[2, 3, 5]);
1127 assert_eq!(sgen.get_index(), 0);
1128 sgen.pop();
1129 assert_eq!(sgen.get_index(), 1);
1130 }
1131
1132 #[test]
1133 fn test_spheren_get_index() {
1134 let mut sgen = SphereN::new(&[2, 3, 5, 7]);
1135 assert_eq!(sgen.get_index(), 0);
1136 sgen.pop();
1137 assert_eq!(sgen.get_index(), 1);
1138 }
1139
1140 #[test]
1141 fn test_sphere3_skip() {
1142 let mut sgen = Sphere3::new(&[2, 3, 5]);
1143 sgen.reseed(0);
1144 sgen.advance(5);
1145 let point = sgen.pop();
1146 let radius_sq = point.iter().map(|&x| x * x).sum::<f64>();
1147 assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1148 }
1149
1150 #[test]
1151 fn test_spheren_skip() {
1152 let mut sgen = SphereN::new(&[2, 3, 5, 7]);
1153 sgen.reseed(0);
1154 sgen.advance(5);
1155 let point = sgen.pop();
1156 let radius_sq = point.iter().map(|&x| x * x).sum::<f64>();
1157 assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1158 }
1159
1160 #[test]
1161 fn test_struct_sizes() {
1162 use std::mem::size_of;
1163 assert_eq!(size_of::<Sphere3>(), 120);
1164 assert_eq!(size_of::<SphereN>(), 96);
1165 }
1166
1167 #[test]
1168 fn test_arc_sharing() {
1169 let _s1 = Sphere3::new(&[2, 3, 5]);
1170 let _s2 = Sphere3::new(&[3, 5, 7]);
1171 assert_eq!(SPHERE_TABLES.x.len(), 300);
1172
1173 let tp4 = tp_cache_get(3);
1174 let _n1 = SphereN::new(&[2, 3, 5, 7]);
1175 let _n2 = SphereN::new(&[3, 5, 7, 11]);
1176 assert_eq!(tp4.len(), 300);
1177 }
1178}