Skip to main content

lds_rs/
lib.rs

1//! Low-Discrepancy Sequence (LDS) Generator
2//!
3//! This library implements a set of low-discrepancy sequence generators, which are used to create
4//! sequences of numbers that are more evenly distributed than random numbers. These sequences are
5//! particularly useful in various fields such as computer graphics, numerical integration, and
6//! Monte Carlo simulations.
7//!
8//! The library defines several structs, each representing a different type of low-discrepancy sequence generator. The main types of sequences implemented are:
9//!
10//! 1. van der Corput sequence
11//! 2. Halton sequence
12//! 3. Circle sequence
13//! 4. Disk sequence
14//! 5. Sphere sequence
15//! 6. 3-Sphere Hopf sequence
16//! 7. N-dimensional Halton sequence
17//!
18//! Each generator takes specific inputs, usually in the form of base numbers or sequences of base numbers. These bases determine how the sequences are generated. The generators produce outputs in the form of floating-point numbers or vectors of floating-point numbers, depending on the dimensionality of the sequence.
19//!
20//! The core algorithm used in most of these generators is the van der Corput sequence. This sequence
21//! is created by expressing integers in a given base, reversing the digits, and placing them after a
22//! decimal point. For example, in base 2, the sequence would start: 1/2, 1/4, 3/4, 1/8, 5/8, and so on.
23//!
24//! The Halton sequence extends this concept to multiple dimensions by using a different base for each
25//! dimension. The Circle and Sphere sequences use trigonometric functions to map these
26//! low-discrepancy sequences onto circular or spherical surfaces.
27//!
28//! The library also includes utility functions and constants to support these generators. For instance, there's a list of prime numbers that can be used as bases for the sequences.
29//!
30//! Each generator struct has methods to produce the next value in the sequence (`pop()`) and to reset the sequence to a specific starting point (`reseed()`). This allows for flexible use of the generators in various applications.
31//!
32//! The purpose of this library is to provide a toolkit for generating well-distributed sequences of
33//! numbers, which can be used in place of random numbers in many applications to achieve more uniform
34//! coverage of a given space or surface. This can lead to more efficient and accurate results in
35//! tasks like sampling, integration, and optimization.
36//!
37//! ![Halton 2D scatter plot][halton-2d-scatter]
38//!
39//! *Figure: 500 points of a 2D Halton sequence with bases 2 and 3.*
40//!
41//! The image above is embedded via `embed-doc-image`. To build docs with images locally:
42//!
43//! ```bash
44//! cargo doc --features doc-images --open
45//! ```
46//!
47//! On docs.rs, images are rendered automatically.
48//!
49//! # Visual examples
50//!
51//! | Generator | Visualisation |
52//! |---|---|
53//! | **Van der Corput** (base 2) — 64 values | ![VdC step sequence][vdc-sequence] |
54//! | **Circle** (base 2) — 200 points | ![Circle points][circle-points] |
55//! | **Disk** (bases 2, 3) — 200 points | ![Disk points][disk-points] |
56//! | **Halton 2D** (bases 2, 3) — 500 points | ![Halton 2D scatter][halton-2d-scatter] |
57
58// Embed images for docs (feature-gated for rustdoc compatibility).
59// Image paths are relative to the crate root.
60#![cfg_attr(
61    feature = "doc-images",
62    doc = embed_doc_image::embed_image!("vdc-sequence", "docs/images/vdc-sequence.png")
63)]
64#![cfg_attr(
65    feature = "doc-images",
66    doc = embed_doc_image::embed_image!("circle-points", "docs/images/circle-points.png")
67)]
68#![cfg_attr(
69    feature = "doc-images",
70    doc = embed_doc_image::embed_image!("disk-points", "docs/images/disk-points.png")
71)]
72#![cfg_attr(
73    feature = "doc-images",
74    doc = embed_doc_image::embed_image!("halton-2d-scatter", "docs/images/halton-2d-scatter.png")
75)]
76
77use std::f64::consts::PI;
78use std::sync::atomic::{AtomicU64, Ordering};
79
80/// Constant for 2π
81pub const TWO_PI: f64 = 2.0 * PI;
82
83/// Maximum number of digits for van der Corput sequence
84pub const MAX_DIGITS: usize = 64;
85
86/// van der Corput sequence function
87///
88/// Converts an integer to its radical inverse by reversing its base-$$b$$ expansion:
89///
90/// $$ n = \sum_{k=0}^{m} d_k b^k \quad\longrightarrow\quad \phi_b(n) = \sum_{k=0}^{m} \frac{d_k}{b^{k+1}} $$
91///
92/// where $$ d_k \in \{0, 1, \dots, b-1\} $$ are the base-$$b$$ digits of $$n$$.
93///
94/// # Arguments
95///
96/// * `count` - The number for which we want to calculate the van der Corput sequence value
97/// * `base` - The base of the number system being used (defaults to 2)
98///
99/// # Examples
100///
101/// ```
102/// use lds_rs::vdc;
103/// assert_eq!(vdc(11, 2), 0.8125);
104/// ```
105pub fn vdc(count: u64, base: u64) -> f64 {
106    let mut count = count;
107    let mut reslt = 0.0;
108    let mut denom = 1.0;
109    let base_f64 = base as f64;
110
111    while count != 0 {
112        denom *= base_f64;
113        let remainder = (count % base) as f64;
114        count /= base;
115        reslt += remainder / denom;
116    }
117    reslt
118}
119
120/// van der Corput sequence generator
121///
122/// Generates the van der Corput sequence, a low-discrepancy sequence commonly used in
123/// quasi-Monte Carlo methods. The sequence is generated by iterating over a
124/// base and calculating the fractional part of the number in that base.
125///
126/// The radical inverse process reverses the base-$$b$$ digits of the integer.
127/// For example, $$n = 13 = 1101_2$$, reversed digits give $$\phi_2(13) = 0.1011_2 = 0.6875$$.
128///
129/// # Examples
130///
131/// ```
132/// use lds_rs::VdCorput;
133/// let mut vgen = VdCorput::new(2);
134/// vgen.reseed(0);
135/// assert_eq!(vgen.pop(), 0.5);
136/// assert_eq!(vgen.pop(), 0.25);
137/// assert_eq!(vgen.pop(), 0.75);
138/// ```
139#[cfg_attr(feature = "doc-images", doc = svgbobdoc::transform!(
140/// ```svgbob
141///      .──────────────.
142///      │  n = 13₁₀    │
143///      │  ≡ 1101₂     │
144///      '──────┬───────'
145///             │ reverse base-2 digits
146///             ▼
147///      .──────────────.
148///      │  reversed:   │
149///      │  1011₂       │
150///      │  as decimal: │
151///      │  0.1011₂     │
152///      │  = 0.6875    │
153///      '──────────────'
154/// ```
155))]
156#[derive(Debug)]
157pub struct VdCorput {
158    count: AtomicU64,
159    base: u64,
160    rev_lst: Vec<f64>,
161}
162
163impl VdCorput {
164    /// Creates a new van der Corput sequence generator with the given base
165    ///
166    /// # Arguments
167    ///
168    /// * `base` - The base of the number system (defaults to 2 if not specified)
169    pub fn new(base: u64) -> Self {
170        assert!(base >= 2, "base must be >= 2, got {}", base);
171        let mut rev_lst = Vec::with_capacity(MAX_DIGITS);
172        let mut reverse = 1.0;
173        let base_f64 = base as f64;
174
175        for _ in 0..MAX_DIGITS {
176            reverse /= base_f64;
177            rev_lst.push(reverse);
178        }
179
180        Self {
181            count: AtomicU64::new(0),
182            base,
183            rev_lst,
184        }
185    }
186
187    /// Generates the next value in the sequence
188    ///
189    /// $$ \phi_b(n) = \sum_{k=0}^{m} \frac{d_k}{b^{k+1}} $$
190    ///
191    /// Increments the count and calculates the van der Corput sequence value
192    /// for that count and base.
193    pub fn pop(&mut self) -> f64 {
194        let count = self.count.fetch_add(1, Ordering::Relaxed) + 1; // ignore 0
195        let mut count = count;
196        let mut res = 0.0;
197        let mut i = 0;
198
199        while count != 0 {
200            let remainder = (count % self.base) as f64;
201            count /= self.base;
202            if remainder != 0.0 {
203                res += remainder * self.rev_lst[i];
204            }
205            i += 1;
206        }
207        res
208    }
209
210    /// Returns the next value without advancing the state (peek)
211    ///
212    /// $$ \phi_b(n) = \sum_{k=0}^{m} \frac{d_k}{b^{k+1}} $$
213    ///
214    /// Allows looking at the next value in the sequence without consuming it.
215    pub fn peek(&self) -> f64 {
216        let mut count = self.count.load(Ordering::Relaxed) + 1;
217        let mut res = 0.0;
218        let mut i = 0;
219
220        while count != 0 {
221            let remainder = (count % self.base) as f64;
222            count /= self.base;
223            if remainder != 0.0 {
224                res += remainder * self.rev_lst[i];
225            }
226            i += 1;
227        }
228        res
229    }
230
231    /// Advances the sequence by `n` values without computing them
232    ///
233    /// # Arguments
234    ///
235    /// * `n` - The number of values to advance
236    pub fn advance(&self, n: u64) {
237        self.count.fetch_add(n, Ordering::Relaxed);
238    }
239
240    /// Returns the current index (number of values generated so far)
241    pub fn get_index(&self) -> u64 {
242        self.count.load(Ordering::Relaxed)
243    }
244
245    /// Resets the state of the sequence generator to a specific seed value
246    ///
247    /// # Arguments
248    ///
249    /// * `seed` - The seed value that determines the starting point of the sequence generation
250    pub fn reseed(&mut self, seed: u64) {
251        self.count.store(seed, Ordering::Relaxed);
252    }
253}
254
255impl Iterator for VdCorput {
256    type Item = f64;
257
258    /// Returns the next value in the sequence
259    ///
260    /// This allows VdCorput to be used with iterator methods like `.take()`, `.collect()`, etc.
261    fn next(&mut self) -> Option<Self::Item> {
262        Some(self.pop())
263    }
264}
265
266impl Default for VdCorput {
267    fn default() -> Self {
268        Self::new(2)
269    }
270}
271
272impl Clone for VdCorput {
273    /// Creates a deep copy of the van der Corput generator
274    ///
275    /// The cloned generator starts with the same internal state as the original,
276    /// including the current count, base, and reversed digit list.
277    fn clone(&self) -> Self {
278        Self {
279            count: AtomicU64::new(self.count.load(Ordering::Relaxed)),
280            base: self.base,
281            rev_lst: self.rev_lst.clone(),
282        }
283    }
284}
285
286/// Halton sequence generator
287///
288/// Generates points in a 2-dimensional space using the Halton sequence.
289/// The Halton sequence is a low-discrepancy sequence that is often used in
290/// quasi-Monte Carlo methods. It is generated by iterating over two different
291/// bases and calculating the fractional parts of the numbers in those bases.
292///
293/// # Examples
294///
295/// ```
296/// use lds_rs::Halton;
297/// let mut hgen = Halton::new([2, 3]);
298/// hgen.reseed(0);
299/// let res = hgen.pop();
300/// assert_eq!(res[0], 0.5);
301/// assert!((res[1] - 1.0/3.0).abs() < 1e-10);
302/// ```
303#[cfg_attr(feature = "doc-images", doc = svgbobdoc::transform!(
304/// ```svgbob
305///  .───────────.     .───────────.
306///  │ VdC(2, n) │────►│ x_coord   │
307///  '───────────'     '───────────'
308///  .───────────.     .───────────.
309///  │ VdC(3, n) │────►│ y_coord   │
310///  '───────────'     '───────────'
311/// ```
312))]
313pub struct Halton {
314    vdc0: VdCorput,
315    vdc1: VdCorput,
316}
317
318impl Halton {
319    /// Creates a new Halton sequence generator with the given bases
320    ///
321    /// # Arguments
322    ///
323    /// * `base` - An array of two integers used as bases for generating the Halton sequence
324    pub fn new(base: [u64; 2]) -> Self {
325        Self {
326            vdc0: VdCorput::new(base[0]),
327            vdc1: VdCorput::new(base[1]),
328        }
329    }
330
331    /// Generates the next point in the Halton sequence
332    ///
333    /// Returns the next point in the Halton sequence as a `[f64; 2]`.
334    pub fn pop(&mut self) -> [f64; 2] {
335        [self.vdc0.pop(), self.vdc1.pop()]
336    }
337
338    /// Returns the next point without advancing the state (peek)
339    pub fn peek(&self) -> [f64; 2] {
340        [self.vdc0.peek(), self.vdc1.peek()]
341    }
342
343    /// Skips `n` points in the sequence without computing them
344    ///
345    /// # Arguments
346    ///
347    /// * `n` - The number of points to skip
348    pub fn advance(&self, n: u64) {
349        self.vdc0.advance(n);
350        self.vdc1.advance(n);
351    }
352
353    /// Returns the current index (number of points generated so far)
354    pub fn get_index(&self) -> u64 {
355        self.vdc0.get_index()
356    }
357
358    /// Resets the state of the sequence generator to a specific seed value
359    ///
360    /// # Arguments
361    ///
362    /// * `seed` - The seed value that determines the starting point of the sequence generation
363    pub fn reseed(&mut self, seed: u64) {
364        self.vdc0.reseed(seed);
365        self.vdc1.reseed(seed);
366    }
367}
368
369impl Iterator for Halton {
370    type Item = [f64; 2];
371
372    /// Returns the next point in the Halton sequence
373    ///
374    /// This allows Halton to be used with iterator methods like `.take()`, `.collect()`, etc.
375    fn next(&mut self) -> Option<Self::Item> {
376        Some(self.pop())
377    }
378}
379
380impl Clone for Halton {
381    /// Creates a deep copy of the Halton generator
382    ///
383    /// The cloned generator contains cloned VdCorput instances for each dimension,
384    /// preserving the same internal state.
385    fn clone(&self) -> Self {
386        Self {
387            vdc0: self.vdc0.clone(),
388            vdc1: self.vdc1.clone(),
389        }
390    }
391}
392/// Unit Circle sequence generator
393///
394/// Generates points on the unit circle using a low-discrepancy sequence.
395///
396/// Maps a van der Corput value to an angle $$\theta = 2\pi v$$ and returns
397/// $$(\cos\theta,\; \sin\theta)$$ on the unit circle $$S^1$$.
398///
399/// # Examples
400///
401/// ```
402/// use lds_rs::Circle;
403/// let mut cgen = Circle::new(2);
404/// cgen.reseed(0);
405/// let res = cgen.pop();
406/// // Should be approximately [-1.0, 0.0] (cos(π), sin(π))
407/// assert!((res[0] + 1.0).abs() < 1e-10);
408/// assert!(res[1].abs() < 1e-10);
409/// ```
410#[cfg_attr(feature = "doc-images", doc = svgbobdoc::transform!(
411/// ```svgbob
412///  .───────────.        .───────────────.
413///  │ φ₂(n)     │───────►│ θ = 2π·φ₂(n) │
414///  │ ∈ [0, 1)  │   θ    │               │
415///  '───────────'        '───────┬───────'
416///                               │
417///                               ▼
418///                        .───────────────.
419///                        │ (cos θ, sin θ)│
420///                        │  on unit S¹   │
421///                        '───────────────'
422/// ```
423))]
424pub struct Circle {
425    vdc: VdCorput,
426}
427
428impl Circle {
429    /// Creates a new Circle sequence generator with the given base
430    ///
431    /// # Arguments
432    ///
433    /// * `base` - The base of the van der Corput sequence
434    pub fn new(base: u64) -> Self {
435        assert!(base >= 2, "base must be >= 2, got {}", base);
436        Self {
437            vdc: VdCorput::new(base),
438        }
439    }
440
441    /// Generates the next point on the unit circle
442    ///
443    /// $$ \theta = 2\pi v, \qquad (\cos\theta,\; \sin\theta) $$
444    ///
445    /// Maps the van der Corput value $$ v \in \[0,1\] $$ to $$ \[0, 2\pi\] $$.
446    ///
447    /// Returns the next point on the unit circle as a `[f64; 2]`.
448    pub fn pop(&mut self) -> [f64; 2] {
449        let theta = self.vdc.pop() * TWO_PI; // map to [0, 2π]
450        [theta.cos(), theta.sin()]
451    }
452
453    /// Returns the next point without advancing the state (peek)
454    ///
455    /// $$ \theta = 2\pi v, \qquad (\cos\theta,\; \sin\theta) $$
456    pub fn peek(&self) -> [f64; 2] {
457        let theta = self.vdc.peek() * TWO_PI;
458        [theta.cos(), theta.sin()]
459    }
460
461    /// Skips `n` points in the sequence without computing them
462    ///
463    /// # Arguments
464    ///
465    /// * `n` - The number of points to skip
466    pub fn advance(&self, n: u64) {
467        self.vdc.advance(n);
468    }
469
470    /// Returns the current index (number of points generated so far)
471    pub fn get_index(&self) -> u64 {
472        self.vdc.get_index()
473    }
474
475    /// Resets the state of the sequence generator to a specific seed value
476    ///
477    /// # Arguments
478    ///
479    /// * `seed` - The seed value that determines the starting point of the sequence generation
480    pub fn reseed(&mut self, seed: u64) {
481        self.vdc.reseed(seed);
482    }
483}
484
485impl Iterator for Circle {
486    type Item = [f64; 2];
487
488    /// Returns the next point on the unit circle
489    ///
490    /// This allows Circle to be used with iterator methods like `.take()`, `.collect()`, etc.
491    fn next(&mut self) -> Option<Self::Item> {
492        Some(self.pop())
493    }
494}
495
496impl Clone for Circle {
497    fn clone(&self) -> Self {
498        Self {
499            vdc: self.vdc.clone(),
500        }
501    }
502}
503
504/// Unit Disk sequence generator
505///
506/// Generates points in the unit disk using a low-discrepancy sequence.
507///
508/// Maps two van der Corput values to polar coordinates:
509/// $$\theta = 2\pi v_\theta$$ and $$r = \sqrt{v_r}$$, returning
510/// $$(r\cos\theta,\; r\sin\theta)$$ inside the unit disk.
511///
512/// # Examples
513///
514/// ```
515/// use lds_rs::Disk;
516/// let mut dgen = Disk::new([2, 3]);
517/// dgen.reseed(0);
518/// let res = dgen.pop();
519/// // First point should be on the unit disk
520/// let radius_sq = res[0] * res[0] + res[1] * res[1];
521/// assert!(radius_sq <= 1.0);
522/// ```
523#[cfg_attr(feature = "doc-images", doc = svgbobdoc::transform!(
524/// ```svgbob
525///  .───────────.   θ = 2π·φ₂(n)
526///  │ φ₂(n)     │───► angle θ
527///  '───────────'
528///  .───────────.   r = √φ₃(n)
529///  │ φ₃(n)     │───► radius r
530///  '───────────'
531///
532///       combine:
533///   (r·cosθ, r·sinθ)
534///         │
535///         ▼
536///   .───────────────.
537///   │ point inside  │
538///   │ unit disk D²  │
539///   '───────────────'
540/// ```
541))]
542pub struct Disk {
543    vdc0: VdCorput,
544    vdc1: VdCorput,
545}
546
547impl Disk {
548    /// Creates a new Disk sequence generator with the given bases
549    ///
550    /// # Arguments
551    ///
552    /// * `base` - An array of two integers used as bases for generating the sequence
553    pub fn new(base: [u64; 2]) -> Self {
554        Self {
555            vdc0: VdCorput::new(base[0]),
556            vdc1: VdCorput::new(base[1]),
557        }
558    }
559
560    /// Generates the next point in the unit disk
561    ///
562    /// $$ \theta = 2\pi v_\theta, \qquad r = \sqrt{v_r}, \qquad (r\cos\theta,\; r\sin\theta) $$
563    ///
564    /// Returns the next point in the unit disk as a `[f64; 2]`.
565    pub fn pop(&mut self) -> [f64; 2] {
566        let theta = self.vdc0.pop() * TWO_PI; // map to [0, 2π]
567        let radius = self.vdc1.pop().sqrt(); // map to [0, 1]
568        [radius * theta.cos(), radius * theta.sin()]
569    }
570
571    /// Returns the next point without advancing the state (peek)
572    ///
573    /// $$ \theta = 2\pi v_\theta, \qquad r = \sqrt{v_r}, \qquad (r\cos\theta,\; r\sin\theta) $$
574    pub fn peek(&self) -> [f64; 2] {
575        let theta = self.vdc0.peek() * TWO_PI;
576        let radius = self.vdc1.peek().sqrt();
577        [radius * theta.cos(), radius * theta.sin()]
578    }
579
580    /// Skips `n` points in the sequence without computing them
581    ///
582    /// # Arguments
583    ///
584    /// * `n` - The number of points to skip
585    pub fn advance(&self, n: u64) {
586        self.vdc0.advance(n);
587        self.vdc1.advance(n);
588    }
589
590    /// Returns the current index (number of points generated so far)
591    pub fn get_index(&self) -> u64 {
592        self.vdc0.get_index()
593    }
594
595    /// Resets the state of the sequence generator to a specific seed value
596    ///
597    /// # Arguments
598    ///
599    /// * `seed` - The seed value that determines the starting point of the sequence generation
600    pub fn reseed(&mut self, seed: u64) {
601        self.vdc0.reseed(seed);
602        self.vdc1.reseed(seed);
603    }
604}
605
606impl Iterator for Disk {
607    type Item = [f64; 2];
608
609    /// Returns the next point in the unit disk
610    ///
611    /// This allows Disk to be used with iterator methods like `.take()`, `.collect()`, etc.
612    fn next(&mut self) -> Option<Self::Item> {
613        Some(self.pop())
614    }
615}
616
617impl Clone for Disk {
618    fn clone(&self) -> Self {
619        Self {
620            vdc0: self.vdc0.clone(),
621            vdc1: self.vdc1.clone(),
622        }
623    }
624}
625
626/// Unit Sphere sequence generator
627///
628/// Generates points on the unit sphere using a low-discrepancy sequence.
629///
630/// Uses a cylindrical equal-area projection from two van der Corput values:
631/// $$z = 2v_z - 1$$ and $$\theta = 2\pi v_\theta$$, returning
632/// $$(\sqrt{1-z^2}\cos\theta,\; \sqrt{1-z^2}\sin\theta,\; z)$$ on $$S^2$$.
633///
634/// # Examples
635///
636/// ```
637/// use lds_rs::Sphere;
638/// let mut sgen = Sphere::new([2, 3]);
639/// sgen.reseed(0);
640/// let res = sgen.pop();
641/// // Should be on the unit sphere
642/// let radius_sq = res[0] * res[0] + res[1] * res[1] + res[2] * res[2];
643/// assert!((radius_sq - 1.0).abs() < 1e-10);
644/// ```
645#[cfg_attr(feature = "doc-images", doc = svgbobdoc::transform!(
646/// ```svgbob
647///  .───────────.   z = 2·φ₂(n) - 1
648///  │ φ₂(n)     │───► z ∈ [-1, 1]
649///  '───────────'
650///  .───────────.   θ = 2π·φ₃(n)
651///  │ φ₃(n)     │───► angle θ
652///  '───────────'
653///
654///          │
655///          ▼
656///  .──────────────────────────.
657///  │ ( √(1-z²)·cosθ,          │
658///  │   √(1-z²)·sinθ,          │
659///  │   z )                    │
660///  │ point on unit sphere S²  │
661///  '──────────────────────────'
662/// ```
663))]
664pub struct Sphere {
665    vdcgen: VdCorput,
666    cirgen: Circle,
667}
668
669impl Sphere {
670    /// Creates a new Sphere sequence generator with the given bases
671    ///
672    /// # Arguments
673    ///
674    /// * `base` - An array of two integers used as bases for generating the sequence
675    pub fn new(base: [u64; 2]) -> Self {
676        Self {
677            vdcgen: VdCorput::new(base[0]),
678            cirgen: Circle::new(base[1]),
679        }
680    }
681
682    /// Generates the next point on the unit sphere
683    ///
684    /// $$ \phi = 2v - 1,\quad v \in \[0,1\], \qquad (\sqrt{1-\phi^2}\cos\theta,\; \sqrt{1-\phi^2}\sin\theta,\; \phi) $$
685    ///
686    /// where $$ \theta = 2\pi v_\theta $$ comes from the Circle generator.
687    /// This is a cylindrical equal-area projection.
688    ///
689    /// Returns the next point on the unit sphere as a `[f64; 3]`.
690    pub fn pop(&mut self) -> [f64; 3] {
691        let cosphi = 2.0 * self.vdcgen.pop() - 1.0; // map to [-1, 1]
692        let sinphi = (1.0 - cosphi * cosphi).sqrt(); // cylindrical mapping
693        let [cos, sin] = self.cirgen.pop();
694        [sinphi * cos, sinphi * sin, cosphi]
695    }
696
697    /// Returns the next point without advancing the state (peek)
698    ///
699    /// $$ \phi = 2v - 1, \qquad (\sqrt{1-\phi^2}\cos\theta,\; \sqrt{1-\phi^2}\sin\theta,\; \phi) $$
700    pub fn peek(&self) -> [f64; 3] {
701        let cosphi = 2.0 * self.vdcgen.peek() - 1.0;
702        let sinphi = (1.0 - cosphi * cosphi).sqrt();
703        let [cos, sin] = self.cirgen.peek();
704        [sinphi * cos, sinphi * sin, cosphi]
705    }
706
707    /// Skips `n` points in the sequence without computing them
708    ///
709    /// # Arguments
710    ///
711    /// * `n` - The number of points to skip
712    pub fn advance(&self, n: u64) {
713        self.cirgen.advance(n);
714        self.vdcgen.advance(n);
715    }
716
717    /// Returns the current index (number of points generated so far)
718    pub fn get_index(&self) -> u64 {
719        self.vdcgen.get_index()
720    }
721
722    /// Resets the state of the sequence generator to a specific seed value
723    ///
724    /// # Arguments
725    ///
726    /// * `seed` - The seed value that determines the starting point of the sequence generation
727    pub fn reseed(&mut self, seed: u64) {
728        self.cirgen.reseed(seed);
729        self.vdcgen.reseed(seed);
730    }
731}
732
733impl Iterator for Sphere {
734    type Item = [f64; 3];
735
736    /// Returns the next point on the unit sphere
737    ///
738    /// This allows Sphere to be used with iterator methods like `.take()`, `.collect()`, etc.
739    fn next(&mut self) -> Option<Self::Item> {
740        Some(self.pop())
741    }
742}
743
744impl Clone for Sphere {
745    fn clone(&self) -> Self {
746        Self {
747            vdcgen: self.vdcgen.clone(),
748            cirgen: self.cirgen.clone(),
749        }
750    }
751}
752
753/// Sphere-3 sequence generator using Hopf coordinates
754///
755/// Based on the paper:
756/// Yershova, Anna, et al. "Generating uniform incremental grids on SO (3) using the Hopf fibration."
757/// The International journal of robotics research 29.7 (2010): 801-812.
758///
759/// The Hopf fibration parametrises the 3-sphere $$S^3$$ using three VdC values:
760/// $$\eta = \sqrt{v}$$, $$\psi = 2\pi v_\psi$$, $$\phi = 2\pi v_\phi$$,
761/// producing $$(\cos\eta\cdot e^{i\psi},\; \sin\eta\cdot e^{i(\phi+\psi)})$$.
762///
763/// # Examples
764///
765/// ```
766/// use lds_rs::Sphere3Hopf;
767/// let mut sp3hgen = Sphere3Hopf::new([2, 3, 5]);
768/// sp3hgen.reseed(0);
769/// let res = sp3hgen.pop();
770/// // Should be on the 3-sphere
771/// let radius_sq = res[0] * res[0] + res[1] * res[1] + res[2] * res[2] + res[3] * res[3];
772/// assert!((radius_sq - 1.0).abs() < 1e-10);
773/// ```
774#[cfg_attr(feature = "doc-images", doc = svgbobdoc::transform!(
775/// ```svgbob
776///  .───────────.  η = √φ₅(n)
777///  │ φ₅(n)     │───► cos η, sin η (Hopf angle)
778///  '───────────'
779///  .───────────.  ψ = 2π·φ₂(n)
780///  │ φ₂(n)     │───► ψ (Hopf phase)
781///  '───────────'
782///  .───────────.  φ = 2π·φ₃(n)
783///  │ φ₃(n)     │───► φ (rotation)
784///  '───────────'
785///
786///      combine via Hopf fibration:
787///
788///  S³ = (cosη·e^iψ,  sinη·e^i(φ+ψ))
789///               │
790///               ▼
791///      .──────────────────.
792///      │ 4D point on      │
793///      │ unit 3-sphere S³ │
794///      '──────────────────'
795/// ```
796))]
797pub struct Sphere3Hopf {
798    vdc0: VdCorput,
799    vdc1: VdCorput,
800    vdc2: VdCorput,
801}
802
803impl Sphere3Hopf {
804    /// Creates a new Sphere3Hopf sequence generator with the given bases
805    ///
806    /// # Arguments
807    ///
808    /// * `base` - An array of three integers used as bases for generating the sequence
809    pub fn new(base: [u64; 3]) -> Self {
810        Self {
811            vdc0: VdCorput::new(base[0]),
812            vdc1: VdCorput::new(base[1]),
813            vdc2: VdCorput::new(base[2]),
814        }
815    }
816
817    /// Generates the next point on the 3-sphere using Hopf fibration
818    ///
819    /// The 3-sphere $$ S^3 $$ is parameterised by the Hopf fibration:
820    ///
821    /// $$ (\cos\eta\cdot e^{i\psi},\; \sin\eta\cdot e^{i(\phi+\psi)}) $$
822    ///
823    /// where $$ \phi,\psi \in [0, 2\pi) $$ and $$ \eta = \sqrt{v} $$ with
824    /// $$ v \in \[0,1\] $$ (stratified sampling for uniform measure on $$ S^3 $$).
825    ///
826    /// Returns the next point on the 3-sphere as a `[f64; 4]`.
827    pub fn pop(&mut self) -> [f64; 4] {
828        let phi = self.vdc0.pop() * TWO_PI; // map to [0, 2π]
829        let psy = self.vdc1.pop() * TWO_PI; // map to [0, 2π]
830        let vdc = self.vdc2.pop();
831        let cos_eta = vdc.sqrt();
832        let sin_eta = (1.0 - vdc).sqrt();
833        [
834            cos_eta * psy.cos(),
835            cos_eta * psy.sin(),
836            sin_eta * (phi + psy).cos(),
837            sin_eta * (phi + psy).sin(),
838        ]
839    }
840
841    /// Returns the next point without advancing the state (peek)
842    ///
843    /// $$ (\cos\eta\cdot e^{i\psi},\; \sin\eta\cdot e^{i(\phi+\psi)}) $$
844    pub fn peek(&self) -> [f64; 4] {
845        let phi = self.vdc0.peek() * TWO_PI;
846        let psy = self.vdc1.peek() * TWO_PI;
847        let vdc = self.vdc2.peek();
848        let cos_eta = vdc.sqrt();
849        let sin_eta = (1.0 - vdc).sqrt();
850        [
851            cos_eta * psy.cos(),
852            cos_eta * psy.sin(),
853            sin_eta * (phi + psy).cos(),
854            sin_eta * (phi + psy).sin(),
855        ]
856    }
857
858    /// Skips `n` points in the sequence without computing them
859    ///
860    /// # Arguments
861    ///
862    /// * `n` - The number of points to skip
863    pub fn advance(&self, n: u64) {
864        self.vdc0.advance(n);
865        self.vdc1.advance(n);
866        self.vdc2.advance(n);
867    }
868
869    /// Returns the current index (number of points generated so far)
870    pub fn get_index(&self) -> u64 {
871        self.vdc0.get_index()
872    }
873
874    /// Resets the state of the sequence generator to a specific seed value
875    ///
876    /// # Arguments
877    ///
878    /// * `seed` - The seed value that determines the starting point of the sequence generation
879    pub fn reseed(&mut self, seed: u64) {
880        self.vdc0.reseed(seed);
881        self.vdc1.reseed(seed);
882        self.vdc2.reseed(seed);
883    }
884}
885
886impl Iterator for Sphere3Hopf {
887    type Item = [f64; 4];
888
889    /// Returns the next point on the 3-sphere
890    ///
891    /// This allows Sphere3Hopf to be used with iterator methods like `.take()`, `.collect()`, etc.
892    fn next(&mut self) -> Option<Self::Item> {
893        Some(self.pop())
894    }
895}
896
897impl Clone for Sphere3Hopf {
898    fn clone(&self) -> Self {
899        Self {
900            vdc0: self.vdc0.clone(),
901            vdc1: self.vdc1.clone(),
902            vdc2: self.vdc2.clone(),
903        }
904    }
905}
906
907/// N-dimensional Halton sequence generator
908///
909/// Generates points in N-dimensional space using the Halton sequence.
910///
911/// # Examples
912///
913/// ```
914/// use lds_rs::HaltonN;
915/// let mut hgen = HaltonN::new(&[2, 3, 5]);
916/// hgen.reseed(0);
917/// let res = hgen.pop();
918/// assert_eq!(res[0], 0.5);
919/// assert!((res[1] - 1.0/3.0).abs() < 1e-10);
920/// assert!((res[2] - 0.2).abs() < 1e-10);
921/// ```
922pub struct HaltonN {
923    vdcs: Vec<VdCorput>,
924}
925
926impl HaltonN {
927    /// Creates a new N-dimensional Halton sequence generator with the given bases
928    ///
929    /// # Arguments
930    ///
931    /// * `base` - A slice of integers used as bases for each dimension
932    pub fn new(base: &[u64]) -> Self {
933        let vdcs = base.iter().map(|&b| VdCorput::new(b)).collect();
934        Self { vdcs }
935    }
936
937    /// Generates the next point in the N-dimensional Halton sequence
938    ///
939    /// Returns the next point as a `Vec<f64>`.
940    pub fn pop(&mut self) -> Vec<f64> {
941        let mut result = Vec::with_capacity(self.vdcs.len());
942        for vdc in &mut self.vdcs {
943            result.push(vdc.pop());
944        }
945        result
946    }
947
948    /// Resets the state of the sequence generator to a specific seed value
949    ///
950    /// # Arguments
951    ///
952    /// * `seed` - The seed value that determines the starting point of the sequence generation
953    pub fn reseed(&mut self, seed: u64) {
954        for vdc in &mut self.vdcs {
955            vdc.reseed(seed);
956        }
957    }
958}
959
960impl Iterator for HaltonN {
961    type Item = Vec<f64>;
962
963    /// Returns the next point in the N-dimensional Halton sequence
964    ///
965    /// This allows HaltonN to be used with iterator methods like `.take()`, `.collect()`, etc.
966    fn next(&mut self) -> Option<Self::Item> {
967        Some(self.pop())
968    }
969}
970
971impl Clone for HaltonN {
972    fn clone(&self) -> Self {
973        Self {
974            vdcs: self.vdcs.clone(),
975        }
976    }
977}
978
979/// First 1000 prime numbers
980///
981/// Can be used as bases for low-discrepancy sequences.
982pub const PRIME_TABLE: [u64; 1000] = [
983    2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
984    101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193,
985    197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307,
986    311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421,
987    431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547,
988    557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659,
989    661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797,
990    809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929,
991    937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039,
992    1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153,
993    1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279,
994    1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409,
995    1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499,
996    1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613,
997    1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741,
998    1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873,
999    1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999,
1000    2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113,
1001    2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251,
1002    2267, 2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371,
1003    2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473, 2477,
1004    2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, 2647,
1005    2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731,
1006    2741, 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857,
1007    2861, 2879, 2887, 2897, 2903, 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999, 3001,
1008    3011, 3019, 3023, 3037, 3041, 3049, 3061, 3067, 3079, 3083, 3089, 3109, 3119, 3121, 3137, 3163,
1009    3167, 3169, 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271, 3299,
1010    3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407,
1011    3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533, 3539,
1012    3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, 3643, 3659,
1013    3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793,
1014    3797, 3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, 3911, 3917, 3919,
1015    3923, 3929, 3931, 3943, 3947, 3967, 3989, 4001, 4003, 4007, 4013, 4019, 4021, 4027, 4049, 4051,
1016    4057, 4073, 4079, 4091, 4093, 4099, 4111, 4127, 4129, 4133, 4139, 4153, 4157, 4159, 4177, 4201,
1017    4211, 4217, 4219, 4229, 4231, 4241, 4243, 4253, 4259, 4261, 4271, 4273, 4283, 4289, 4297, 4327,
1018    4337, 4339, 4349, 4357, 4363, 4373, 4391, 4397, 4409, 4421, 4423, 4441, 4447, 4451, 4457, 4463,
1019    4481, 4483, 4493, 4507, 4513, 4517, 4519, 4523, 4547, 4549, 4561, 4567, 4583, 4591, 4597, 4603,
1020    4621, 4637, 4639, 4643, 4649, 4651, 4657, 4663, 4673, 4679, 4691, 4703, 4721, 4723, 4729, 4733,
1021    4751, 4759, 4783, 4787, 4789, 4793, 4799, 4801, 4813, 4817, 4831, 4861, 4871, 4877, 4889, 4903,
1022    4909, 4919, 4931, 4933, 4937, 4943, 4951, 4957, 4967, 4969, 4973, 4987, 4993, 4999, 5003, 5009,
1023    5011, 5021, 5023, 5039, 5051, 5059, 5077, 5081, 5087, 5099, 5101, 5107, 5113, 5119, 5147, 5153,
1024    5167, 5171, 5179, 5189, 5197, 5209, 5227, 5231, 5233, 5237, 5261, 5273, 5279, 5281, 5297, 5303,
1025    5309, 5323, 5333, 5347, 5351, 5381, 5387, 5393, 5399, 5407, 5413, 5417, 5419, 5431, 5437, 5441,
1026    5443, 5449, 5471, 5477, 5479, 5483, 5501, 5503, 5507, 5519, 5521, 5527, 5531, 5557, 5563, 5569,
1027    5573, 5581, 5591, 5623, 5639, 5641, 5647, 5651, 5653, 5657, 5659, 5669, 5683, 5689, 5693, 5701,
1028    5711, 5717, 5737, 5741, 5743, 5749, 5779, 5783, 5791, 5801, 5807, 5813, 5821, 5827, 5839, 5843,
1029    5849, 5851, 5857, 5861, 5867, 5869, 5879, 5881, 5897, 5903, 5923, 5927, 5939, 5953, 5981, 5987,
1030    6007, 6011, 6029, 6037, 6043, 6047, 6053, 6067, 6073, 6079, 6089, 6091, 6101, 6113, 6121, 6131,
1031    6133, 6143, 6151, 6163, 6173, 6197, 6199, 6203, 6211, 6217, 6221, 6229, 6247, 6257, 6263, 6269,
1032    6271, 6277, 6287, 6299, 6301, 6311, 6317, 6323, 6329, 6337, 6343, 6353, 6359, 6361, 6367, 6373,
1033    6379, 6389, 6397, 6421, 6427, 6449, 6451, 6469, 6473, 6481, 6491, 6521, 6529, 6547, 6551, 6553,
1034    6563, 6569, 6571, 6577, 6581, 6599, 6607, 6619, 6637, 6653, 6659, 6661, 6673, 6679, 6689, 6691,
1035    6701, 6703, 6709, 6719, 6733, 6737, 6761, 6763, 6779, 6781, 6791, 6793, 6803, 6823, 6827, 6829,
1036    6833, 6841, 6857, 6863, 6869, 6871, 6883, 6899, 6907, 6911, 6917, 6947, 6949, 6959, 6961, 6967,
1037    6971, 6977, 6983, 6991, 6997, 7001, 7013, 7019, 7027, 7039, 7043, 7057, 7069, 7079, 7103, 7109,
1038    7121, 7127, 7129, 7151, 7159, 7177, 7187, 7193, 7207, 7211, 7213, 7219, 7229, 7237, 7243, 7247,
1039    7253, 7283, 7297, 7307, 7309, 7321, 7331, 7333, 7349, 7351, 7369, 7393, 7411, 7417, 7433, 7451,
1040    7457, 7459, 7477, 7481, 7487, 7489, 7499, 7507, 7517, 7523, 7529, 7537, 7541, 7547, 7549, 7559,
1041    7561, 7573, 7577, 7583, 7589, 7591, 7603, 7607, 7621, 7639, 7643, 7649, 7669, 7673, 7681, 7687,
1042    7691, 7699, 7703, 7717, 7723, 7727, 7741, 7753, 7757, 7759, 7789, 7793, 7817, 7823, 7829, 7841,
1043    7853, 7867, 7873, 7877, 7879, 7883, 7901, 7907, 7919,
1044];
1045
1046/// Integer low-discrepancy sequence generators
1047pub mod ilds;
1048
1049/// N-dimensional sphere sequence generators
1050pub mod sphere_n;
1051
1052#[cfg(test)]
1053mod tests {
1054    use super::*;
1055    use approx::assert_relative_eq;
1056
1057    #[test]
1058    fn test_vdc_function() {
1059        assert_eq!(vdc(11, 2), 0.8125);
1060        assert_eq!(vdc(0, 2), 0.0);
1061        assert_eq!(vdc(1, 2), 0.5);
1062        assert_eq!(vdc(2, 2), 0.25);
1063        assert_eq!(vdc(3, 2), 0.75);
1064    }
1065
1066    #[test]
1067    fn test_vdcorput_pop() {
1068        let mut vgen = VdCorput::new(2);
1069        vgen.reseed(0);
1070        assert_eq!(vgen.pop(), 0.5);
1071        assert_eq!(vgen.pop(), 0.25);
1072        assert_eq!(vgen.pop(), 0.75);
1073        assert_eq!(vgen.pop(), 0.125);
1074    }
1075
1076    #[test]
1077    fn test_vdcorput_reseed() {
1078        let mut vgen = VdCorput::new(2);
1079        vgen.reseed(5);
1080        assert_eq!(vgen.pop(), 0.375);
1081        vgen.reseed(0);
1082        assert_eq!(vgen.pop(), 0.5);
1083    }
1084
1085    #[test]
1086    fn test_vdcorput_default() {
1087        let mut vgen = VdCorput::default();
1088        vgen.reseed(0);
1089        assert_eq!(vgen.pop(), 0.5);
1090        assert_eq!(vgen.pop(), 0.25);
1091    }
1092
1093    #[test]
1094    fn test_halton_pop() {
1095        let mut hgen = Halton::new([2, 3]);
1096        hgen.reseed(0);
1097        let res = hgen.pop();
1098        assert_eq!(res[0], 0.5);
1099        assert_relative_eq!(res[1], 1.0 / 3.0, epsilon = 1e-10);
1100
1101        let res = hgen.pop();
1102        assert_eq!(res[0], 0.25);
1103        assert_relative_eq!(res[1], 2.0 / 3.0, epsilon = 1e-10);
1104    }
1105
1106    #[test]
1107    fn test_circle_pop() {
1108        let mut cgen = Circle::new(2);
1109        cgen.reseed(0);
1110        let res = cgen.pop();
1111        // First point should be at angle π (180 degrees)
1112        assert_relative_eq!(res[0], -1.0, epsilon = 1e-10);
1113        assert_relative_eq!(res[1], 0.0, epsilon = 1e-10);
1114
1115        let res = cgen.pop();
1116        // Second point should be at angle π/2 (90 degrees)
1117        assert_relative_eq!(res[0], 0.0, epsilon = 1e-10);
1118        assert_relative_eq!(res[1], 1.0, epsilon = 1e-10);
1119    }
1120
1121    #[test]
1122    fn test_disk_pop() {
1123        let mut dgen = Disk::new([2, 3]);
1124        dgen.reseed(0);
1125        let res = dgen.pop();
1126
1127        // Check that point is within unit disk
1128        let radius_sq = res[0] * res[0] + res[1] * res[1];
1129        assert!(radius_sq <= 1.0);
1130
1131        // Generate a few more points and check they're all within unit disk
1132        for _ in 0..10 {
1133            let res = dgen.pop();
1134            let radius_sq = res[0] * res[0] + res[1] * res[1];
1135            assert!(radius_sq <= 1.0);
1136        }
1137    }
1138
1139    #[test]
1140    fn test_sphere_pop() {
1141        let mut sgen = Sphere::new([2, 3]);
1142        sgen.reseed(0);
1143        let res = sgen.pop();
1144
1145        // Check that point is on unit sphere
1146        let radius_sq = res[0] * res[0] + res[1] * res[1] + res[2] * res[2];
1147        assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1148
1149        // Generate a few more points and check they're all on unit sphere
1150        for _ in 0..5 {
1151            let res = sgen.pop();
1152            let radius_sq = res[0] * res[0] + res[1] * res[1] + res[2] * res[2];
1153            assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1154        }
1155    }
1156
1157    #[test]
1158    fn test_sphere3hopf_pop() {
1159        let mut sp3hgen = Sphere3Hopf::new([2, 3, 5]);
1160        sp3hgen.reseed(0);
1161        let res = sp3hgen.pop();
1162
1163        // Check that point is on 3-sphere
1164        let radius_sq = res[0] * res[0] + res[1] * res[1] + res[2] * res[2] + res[3] * res[3];
1165        assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1166
1167        // Generate a few more points and check they're all on 3-sphere
1168        for _ in 0..5 {
1169            let res = sp3hgen.pop();
1170            let radius_sq = res[0] * res[0] + res[1] * res[1] + res[2] * res[2] + res[3] * res[3];
1171            assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1172        }
1173    }
1174
1175    #[test]
1176    fn test_haltonn_pop() {
1177        let mut hgen = HaltonN::new(&[2, 3, 5]);
1178        hgen.reseed(0);
1179        let res = hgen.pop();
1180
1181        assert_eq!(res[0], 0.5);
1182        assert_relative_eq!(res[1], 1.0 / 3.0, epsilon = 1e-10);
1183        assert_relative_eq!(res[2], 0.2, epsilon = 1e-10);
1184
1185        let res = hgen.pop();
1186        assert_eq!(res[0], 0.25);
1187        assert_relative_eq!(res[1], 2.0 / 3.0, epsilon = 1e-10);
1188        assert_relative_eq!(res[2], 0.4, epsilon = 1e-10);
1189    }
1190
1191    #[test]
1192    fn test_prime_table() {
1193        // Check first few primes
1194        assert_eq!(PRIME_TABLE[0], 2);
1195        assert_eq!(PRIME_TABLE[1], 3);
1196        assert_eq!(PRIME_TABLE[2], 5);
1197        assert_eq!(PRIME_TABLE[3], 7);
1198        assert_eq!(PRIME_TABLE[4], 11);
1199
1200        // Check table length
1201        assert_eq!(PRIME_TABLE.len(), 1000);
1202
1203        // Check a prime near the end
1204        assert_eq!(PRIME_TABLE[999], 7919);
1205    }
1206
1207    // Additional comprehensive tests for edge cases and different bases
1208
1209    #[test]
1210    fn test_vdc_function_edge_cases() {
1211        // Test with count=0
1212        assert_eq!(vdc(0, 2), 0.0);
1213        assert_eq!(vdc(0, 3), 0.0);
1214        assert_eq!(vdc(0, 5), 0.0);
1215
1216        // Test with count=1
1217        assert_eq!(vdc(1, 2), 0.5);
1218        assert_eq!(vdc(1, 3), 1.0 / 3.0);
1219        assert_eq!(vdc(1, 5), 0.2);
1220
1221        // Test with different bases
1222        assert_eq!(vdc(10, 2), 0.3125); // 1010 in binary -> 0.0101
1223        assert_eq!(vdc(10, 3), 0.37037037037037035); // 101 in base 3 -> 0.101
1224        assert_eq!(vdc(10, 5), 0.08); // 20 in base 5 -> 0.02
1225
1226        // Test with larger numbers
1227        let result = vdc(1000, 2);
1228        assert!((0.0..1.0).contains(&result));
1229
1230        // Test with prime bases
1231        assert_eq!(vdc(6, 7), 6.0 / 7.0);
1232        assert_eq!(vdc(12, 11), 0.09917355371900827);
1233    }
1234
1235    #[test]
1236    fn test_vdcorput_different_bases() {
1237        // Test with base 3
1238        let mut vgen = VdCorput::new(3);
1239        vgen.reseed(0);
1240        assert_relative_eq!(vgen.pop(), 1.0 / 3.0, epsilon = 1e-10);
1241        assert_relative_eq!(vgen.pop(), 2.0 / 3.0, epsilon = 1e-10);
1242        assert_relative_eq!(vgen.pop(), 1.0 / 9.0, epsilon = 1e-10);
1243
1244        // Test with base 5
1245        let mut vgen = VdCorput::new(5);
1246        vgen.reseed(0);
1247        assert_relative_eq!(vgen.pop(), 0.2, epsilon = 1e-10);
1248        assert_relative_eq!(vgen.pop(), 0.4, epsilon = 1e-10);
1249        assert_relative_eq!(vgen.pop(), 0.6, epsilon = 1e-10);
1250        assert_relative_eq!(vgen.pop(), 0.8, epsilon = 1e-10);
1251        assert_relative_eq!(vgen.pop(), 0.04, epsilon = 1e-10);
1252
1253        // Test with base 7
1254        let mut vgen = VdCorput::new(7);
1255        vgen.reseed(0);
1256        assert_relative_eq!(vgen.pop(), 1.0 / 7.0, epsilon = 1e-10);
1257        assert_relative_eq!(vgen.pop(), 2.0 / 7.0, epsilon = 1e-10);
1258    }
1259
1260    #[test]
1261    fn test_vdcorput_large_values() {
1262        let mut vgen = VdCorput::new(2);
1263        vgen.reseed(1000);
1264
1265        // Generate several values and ensure they're valid
1266        for _ in 0..10 {
1267            let value = vgen.pop();
1268            assert!((0.0..1.0).contains(&value));
1269        }
1270    }
1271
1272    #[test]
1273    fn test_halton_different_bases() {
1274        // Test with bases 3 and 5
1275        let mut hgen = Halton::new([3, 5]);
1276        hgen.reseed(0);
1277        let res = hgen.pop();
1278        assert_relative_eq!(res[0], 1.0 / 3.0, epsilon = 1e-10);
1279        assert_relative_eq!(res[1], 0.2, epsilon = 1e-10);
1280
1281        // Test with bases 5 and 7
1282        let mut hgen = Halton::new([5, 7]);
1283        hgen.reseed(0);
1284        let res = hgen.pop();
1285        assert_relative_eq!(res[0], 0.2, epsilon = 1e-10);
1286        assert_relative_eq!(res[1], 1.0 / 7.0, epsilon = 1e-10);
1287    }
1288
1289    #[test]
1290    fn test_circle_different_bases() {
1291        // Test with base 3
1292        let mut cgen = Circle::new(3);
1293        cgen.reseed(0);
1294        let res = cgen.pop();
1295        // Should be on unit circle
1296        let radius_sq = res[0] * res[0] + res[1] * res[1];
1297        assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1298
1299        // Test with base 5
1300        let mut cgen = Circle::new(5);
1301        cgen.reseed(0);
1302        let res = cgen.pop();
1303        let radius_sq = res[0] * res[0] + res[1] * res[1];
1304        assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1305    }
1306
1307    #[test]
1308    fn test_disk_different_bases() {
1309        // Test with bases 3 and 5
1310        let mut dgen = Disk::new([3, 5]);
1311        dgen.reseed(0);
1312        let res = dgen.pop();
1313        let radius_sq = res[0] * res[0] + res[1] * res[1];
1314        assert!(radius_sq <= 1.0);
1315
1316        // Test with bases 5 and 7
1317        let mut dgen = Disk::new([5, 7]);
1318        dgen.reseed(0);
1319        let res = dgen.pop();
1320        let radius_sq = res[0] * res[0] + res[1] * res[1];
1321        assert!(radius_sq <= 1.0);
1322    }
1323
1324    #[test]
1325    fn test_sphere_different_bases() {
1326        // Test with bases 3 and 5
1327        let mut sgen = Sphere::new([3, 5]);
1328        sgen.reseed(0);
1329        let res = sgen.pop();
1330        let radius_sq = res[0] * res[0] + res[1] * res[1] + res[2] * res[2];
1331        assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1332
1333        // Test with bases 5 and 7
1334        let mut sgen = Sphere::new([5, 7]);
1335        sgen.reseed(0);
1336        let res = sgen.pop();
1337        let radius_sq = res[0] * res[0] + res[1] * res[1] + res[2] * res[2];
1338        assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1339    }
1340
1341    #[test]
1342    fn test_sphere3hopf_different_bases() {
1343        // Test with bases 3, 5, 7
1344        let mut sgen = Sphere3Hopf::new([3, 5, 7]);
1345        sgen.reseed(0);
1346        let res = sgen.pop();
1347        let radius_sq = res[0] * res[0] + res[1] * res[1] + res[2] * res[2] + res[3] * res[3];
1348        assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1349
1350        // Test with bases 5, 7, 11
1351        let mut sgen = Sphere3Hopf::new([5, 7, 11]);
1352        sgen.reseed(0);
1353        let res = sgen.pop();
1354        let radius_sq = res[0] * res[0] + res[1] * res[1] + res[2] * res[2] + res[3] * res[3];
1355        assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1356    }
1357
1358    #[test]
1359    fn test_haltonn_different_bases() {
1360        // Test with 4 dimensions
1361        let mut hgen = HaltonN::new(&[3, 5, 7, 11]);
1362        hgen.reseed(0);
1363        let res = hgen.pop();
1364        assert_eq!(res.len(), 4);
1365        assert_relative_eq!(res[0], 1.0 / 3.0, epsilon = 1e-10);
1366        assert_relative_eq!(res[1], 0.2, epsilon = 1e-10);
1367        assert_relative_eq!(res[2], 1.0 / 7.0, epsilon = 1e-10);
1368        assert_relative_eq!(res[3], 1.0 / 11.0, epsilon = 1e-10);
1369
1370        // Test with 5 dimensions
1371        let mut hgen = HaltonN::new(&[2, 3, 5, 7, 11]);
1372        hgen.reseed(0);
1373        let res = hgen.pop();
1374        assert_eq!(res.len(), 5);
1375        assert_relative_eq!(res[0], 0.5, epsilon = 1e-10);
1376        assert_relative_eq!(res[1], 1.0 / 3.0, epsilon = 1e-10);
1377        assert_relative_eq!(res[2], 0.2, epsilon = 1e-10);
1378        assert_relative_eq!(res[3], 1.0 / 7.0, epsilon = 1e-10);
1379        assert_relative_eq!(res[4], 1.0 / 11.0, epsilon = 1e-10);
1380    }
1381
1382    #[test]
1383    fn test_default_implementations() {
1384        // Test Default for VdCorput
1385        let mut vgen = VdCorput::default();
1386        vgen.reseed(0);
1387        assert_eq!(vgen.pop(), 0.5);
1388
1389        // Test that default base is 2
1390        let vgen_default = VdCorput::default();
1391        let vgen_explicit = VdCorput::new(2);
1392        assert_eq!(vgen_default.base, vgen_explicit.base);
1393    }
1394
1395    #[test]
1396    fn test_sequence_properties() {
1397        // Test that VdCorput sequence values are always in [0, 1)
1398        let mut vgen = VdCorput::new(2);
1399        for _ in 0..100 {
1400            let value = vgen.pop();
1401            assert!((0.0..1.0).contains(&value));
1402        }
1403
1404        // Test that Halton sequence values are always in [0, 1) for each dimension
1405        let mut hgen = Halton::new([2, 3]);
1406        for _ in 0..100 {
1407            let res = hgen.pop();
1408            assert!(res[0] >= 0.0 && res[0] < 1.0);
1409            assert!(res[1] >= 0.0 && res[1] < 1.0);
1410        }
1411
1412        // Test that Circle sequence points are always on unit circle
1413        let mut cgen = Circle::new(2);
1414        for _ in 0..100 {
1415            let res = cgen.pop();
1416            let radius_sq = res[0] * res[0] + res[1] * res[1];
1417            assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1418        }
1419
1420        // Test that Disk sequence points are always within unit disk
1421        let mut dgen = Disk::new([2, 3]);
1422        for _ in 0..100 {
1423            let res = dgen.pop();
1424            let radius_sq = res[0] * res[0] + res[1] * res[1];
1425            assert!(radius_sq <= 1.0);
1426        }
1427
1428        // Test that Sphere sequence points are always on unit sphere
1429        let mut sgen = Sphere::new([2, 3]);
1430        for _ in 0..100 {
1431            let res = sgen.pop();
1432            let radius_sq = res[0] * res[0] + res[1] * res[1] + res[2] * res[2];
1433            assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1434        }
1435
1436        // Test that Sphere3Hopf sequence points are always on 3-sphere
1437        let mut sgen = Sphere3Hopf::new([2, 3, 5]);
1438        for _ in 0..100 {
1439            let res = sgen.pop();
1440            let radius_sq = res.iter().map(|&x| x * x).sum::<f64>();
1441            assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1442        }
1443    }
1444
1445    #[test]
1446    fn test_reseed_consistency() {
1447        // Test that reseed with the same value produces the same sequence
1448        let mut vgen = VdCorput::new(2);
1449
1450        vgen.reseed(10);
1451        let seq1: Vec<_> = (0..5).map(|_| vgen.pop()).collect();
1452
1453        vgen.reseed(10);
1454        let seq2: Vec<_> = (0..5).map(|_| vgen.pop()).collect();
1455
1456        for i in 0..5 {
1457            assert_relative_eq!(seq1[i], seq2[i], epsilon = 1e-10);
1458        }
1459
1460        // Test that reseed with different values produces different sequences
1461        vgen.reseed(10);
1462        let seq3: Vec<_> = (0..5).map(|_| vgen.pop()).collect();
1463
1464        vgen.reseed(20);
1465        let seq4: Vec<_> = (0..5).map(|_| vgen.pop()).collect();
1466
1467        let mut different = false;
1468        for i in 0..5 {
1469            if (seq3[i] - seq4[i]).abs() > 1e-10 {
1470                different = true;
1471                break;
1472            }
1473        }
1474        assert!(
1475            different,
1476            "Sequences with different seeds should be different"
1477        );
1478    }
1479
1480    #[test]
1481    fn test_vdcorput_peek() {
1482        let mut vgen = VdCorput::new(2);
1483        vgen.reseed(0);
1484        let peeked = vgen.peek();
1485        assert_relative_eq!(peeked, 0.5, epsilon = 1e-10);
1486        let popped = vgen.pop();
1487        assert_relative_eq!(popped, 0.5, epsilon = 1e-10); // peek doesn't advance
1488        assert_relative_eq!(vgen.peek(), 0.25, epsilon = 1e-10);
1489    }
1490
1491    #[test]
1492    fn test_vdcorput_advance() {
1493        let mut vgen = VdCorput::new(2);
1494        vgen.reseed(0);
1495        vgen.advance(3);
1496        assert_relative_eq!(vgen.pop(), 0.125, epsilon = 1e-10);
1497        vgen.reseed(0);
1498        vgen.advance(4);
1499        // vdc(5, 2) = binary 101 reversed = 0.101 = 0.625
1500        assert_relative_eq!(vgen.pop(), 0.625, epsilon = 1e-10);
1501    }
1502
1503    #[test]
1504    fn test_vdcorput_get_index() {
1505        let mut vgen = VdCorput::new(2);
1506        assert_eq!(vgen.get_index(), 0);
1507        vgen.pop();
1508        assert_eq!(vgen.get_index(), 1);
1509        vgen.pop();
1510        assert_eq!(vgen.get_index(), 2);
1511        vgen.reseed(5);
1512        assert_eq!(vgen.get_index(), 5);
1513    }
1514
1515    #[test]
1516    fn test_halton_peek() {
1517        let mut hgen = Halton::new([2, 3]);
1518        hgen.reseed(0);
1519        let peeked = hgen.peek();
1520        assert_relative_eq!(peeked[0], 0.5, epsilon = 1e-10);
1521        assert_relative_eq!(peeked[1], 1.0 / 3.0, epsilon = 1e-10);
1522        let popped = hgen.pop();
1523        assert_relative_eq!(popped[0], 0.5, epsilon = 1e-10);
1524        assert_relative_eq!(popped[1], 1.0 / 3.0, epsilon = 1e-10);
1525    }
1526
1527    #[test]
1528    fn test_halton_advance() {
1529        let mut hgen = Halton::new([2, 3]);
1530        hgen.reseed(0);
1531        hgen.advance(2);
1532        let popped = hgen.pop();
1533        assert_relative_eq!(popped[0], 0.75, epsilon = 1e-10);
1534        assert_relative_eq!(popped[1], 1.0 / 9.0, epsilon = 1e-10);
1535    }
1536
1537    #[test]
1538    fn test_halton_iterator() {
1539        let mut hgen = Halton::new([2, 3]);
1540        hgen.reseed(0);
1541        let values: Vec<[f64; 2]> = hgen.take(3).collect();
1542        assert_eq!(values.len(), 3);
1543        assert_relative_eq!(values[0][0], 0.5, epsilon = 1e-10);
1544        assert_relative_eq!(values[0][1], 1.0 / 3.0, epsilon = 1e-10);
1545        assert_relative_eq!(values[1][0], 0.25, epsilon = 1e-10);
1546        assert_relative_eq!(values[1][1], 2.0 / 3.0, epsilon = 1e-10);
1547    }
1548
1549    #[test]
1550    fn test_circle_peek() {
1551        let mut cgen = Circle::new(2);
1552        cgen.reseed(0);
1553        let peeked = cgen.peek();
1554        let popped = cgen.pop();
1555        assert_relative_eq!(peeked[0], popped[0], epsilon = 1e-10);
1556        assert_relative_eq!(peeked[1], popped[1], epsilon = 1e-10);
1557    }
1558
1559    #[test]
1560    fn test_circle_advance() {
1561        let mut cgen = Circle::new(2);
1562        cgen.reseed(0);
1563        cgen.advance(5);
1564        let res = cgen.pop();
1565        let radius_sq = res[0] * res[0] + res[1] * res[1];
1566        assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1567    }
1568
1569    #[test]
1570    fn test_circle_get_index() {
1571        let mut cgen = Circle::new(2);
1572        assert_eq!(cgen.get_index(), 0);
1573        cgen.pop();
1574        assert_eq!(cgen.get_index(), 1);
1575    }
1576
1577    #[test]
1578    fn test_disk_peek() {
1579        let mut dgen = Disk::new([2, 3]);
1580        dgen.reseed(0);
1581        let peeked = dgen.peek();
1582        let popped = dgen.pop();
1583        assert_relative_eq!(peeked[0], popped[0], epsilon = 1e-10);
1584        assert_relative_eq!(peeked[1], popped[1], epsilon = 1e-10);
1585    }
1586
1587    #[test]
1588    fn test_disk_advance() {
1589        let mut dgen = Disk::new([2, 3]);
1590        dgen.reseed(0);
1591        dgen.advance(5);
1592        let res = dgen.pop();
1593        let radius_sq = res[0] * res[0] + res[1] * res[1];
1594        assert!(radius_sq <= 1.0);
1595    }
1596
1597    #[test]
1598    fn test_sphere_peek() {
1599        let mut sgen = Sphere::new([2, 3]);
1600        sgen.reseed(0);
1601        let peeked = sgen.peek();
1602        let popped = sgen.pop();
1603        assert_relative_eq!(peeked[0], popped[0], epsilon = 1e-10);
1604        assert_relative_eq!(peeked[1], popped[1], epsilon = 1e-10);
1605        assert_relative_eq!(peeked[2], popped[2], epsilon = 1e-10);
1606    }
1607
1608    #[test]
1609    fn test_sphere_advance() {
1610        let mut sgen = Sphere::new([2, 3]);
1611        sgen.reseed(0);
1612        sgen.advance(5);
1613        let res = sgen.pop();
1614        let radius_sq = res[0] * res[0] + res[1] * res[1] + res[2] * res[2];
1615        assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1616    }
1617
1618    #[test]
1619    fn test_sphere3hopf_peek() {
1620        let mut sgen = Sphere3Hopf::new([2, 3, 5]);
1621        sgen.reseed(0);
1622        let peeked = sgen.peek();
1623        let popped = sgen.pop();
1624        for i in 0..4 {
1625            assert_relative_eq!(peeked[i], popped[i], epsilon = 1e-10);
1626        }
1627    }
1628
1629    #[test]
1630    fn test_sphere3hopf_advance() {
1631        let mut sgen = Sphere3Hopf::new([2, 3, 5]);
1632        sgen.reseed(0);
1633        sgen.advance(5);
1634        let res = sgen.pop();
1635        let radius_sq = res.iter().map(|&x| x * x).sum::<f64>();
1636        assert_relative_eq!(radius_sq, 1.0, epsilon = 1e-10);
1637    }
1638
1639    #[test]
1640    fn test_sphere3hopf_get_index() {
1641        let mut sgen = Sphere3Hopf::new([2, 3, 5]);
1642        assert_eq!(sgen.get_index(), 0);
1643        sgen.pop();
1644        assert_eq!(sgen.get_index(), 1);
1645    }
1646
1647    #[test]
1648    fn test_halton_get_index() {
1649        let mut hgen = Halton::new([2, 3]);
1650        assert_eq!(hgen.get_index(), 0);
1651        hgen.pop();
1652        assert_eq!(hgen.get_index(), 1);
1653    }
1654
1655    #[test]
1656    fn test_circle_clone() {
1657        let mut cgen = Circle::new(2);
1658        cgen.reseed(5);
1659        let mut cloned = cgen.clone();
1660        assert_eq!(cloned.pop(), cgen.pop());
1661    }
1662
1663    #[test]
1664    fn test_disk_clone() {
1665        let mut dgen = Disk::new([2, 3]);
1666        dgen.reseed(5);
1667        let mut cloned = dgen.clone();
1668        assert_eq!(cloned.pop(), dgen.pop());
1669    }
1670
1671    #[test]
1672    fn test_sphere_clone() {
1673        let mut sgen = Sphere::new([2, 3]);
1674        sgen.reseed(5);
1675        let mut cloned = sgen.clone();
1676        assert_eq!(cloned.pop(), sgen.pop());
1677    }
1678
1679    #[test]
1680    fn test_sphere3hopf_clone() {
1681        let mut sgen = Sphere3Hopf::new([2, 3, 5]);
1682        sgen.reseed(5);
1683        let mut cloned = sgen.clone();
1684        assert_eq!(cloned.pop(), sgen.pop());
1685    }
1686
1687    #[test]
1688    fn test_haltonn_clone() {
1689        let mut hgen = HaltonN::new(&[2, 3, 5]);
1690        hgen.reseed(5);
1691        let mut cloned = hgen.clone();
1692        assert_eq!(cloned.pop(), hgen.pop());
1693    }
1694}