Skip to main content

curve_sampling/
lib.rs

1//! This module provide a collection of routines to perform adaptive
2//! sampling of curves as well as manipulating those samplings.
3//!
4//! As of now, the following is available:
5//! - uniform sampling of the graph of functions ℝ → ℝ and the image
6//!   of functions ℝ → ℝ² (see [`Sampling::uniform`]);
7//! - adaptive sampling of the graph functions ℝ → ℝ and the image of
8//!   functions ℝ → ℝ² (see [`Sampling::fun`]).
9//!
10//! Samplings can be saved as a list of coordinates x-y, one point per
11//! line with blank lines to indicate that the path is interrupted,
12//! with [`Sampling::write`].  They can also be saved in a format
13//! usable from the [LaTeX][] package [TikZ][] using
14//! [`Sampling::latex`].  This format allows to add arrows to the
15//! curves to indicate their orientation.
16//!
17//! [LaTeX]: https://www.latex-project.org/
18//! [TikZ]: https://tikz.dev/
19
20use std::{
21    cell::RefCell,
22    cmp::Ordering,
23    fmt::{self, Display, Formatter},
24    io::{self, Write},
25    marker::PhantomData,
26    ops::ControlFlow,
27};
28use rgb::*;
29
30// mod fibonacci_heap;
31// use fibonacci_heap as pq;
32// mod priority_queue;
33// use priority_queue as pq;
34mod approx_priority_queue;
35use approx_priority_queue as pq;
36
37mod linked_list;
38use linked_list as list;
39use list::List;
40
41////////////////////////////////////////////////////////////////////////
42//
43// Bounding box
44
45/// A two dimensional rectangle \[`xmin`, `xmax`\] × \[`ymin`, `ymax`\].
46#[derive(Debug, Clone, Copy, PartialEq)]
47pub struct BoundingBox {
48    pub xmin: f64,
49    pub xmax: f64,
50    pub ymin: f64,
51    pub ymax: f64,
52}
53
54impl BoundingBox {
55    #[inline]
56    pub(crate) fn empty() -> Self {
57        Self { xmin: f64::INFINITY,  xmax: f64::NEG_INFINITY,
58               ymin: f64::INFINITY,  ymax: f64::NEG_INFINITY }
59    }
60
61    /// Return `true` if the bounding box has a non-empty interior.
62    ///
63    /// # Example
64    ///
65    /// ```
66    /// use curve_sampling::Sampling;
67    /// let s = Sampling::from_iter([0.]);
68    /// assert!(s.bounding_box().is_empty());
69    /// ```
70    #[inline]
71    pub fn is_empty(&self) -> bool {
72        !(self.xmin < self.xmax && self.ymin < self.ymax) // NAN ⟹ empty
73    }
74
75    /// Return `true` if the point `p` belongs to the box `self`
76    /// (possibly on the boundary).
77    #[inline]
78    fn contains<D>(&self, p: &Point<D>) -> bool {
79        let [x, y] = p.xy;
80        self.xmin <= x && x <= self.xmax && self.ymin <= y && y <= self.ymax
81    }
82
83    /// Return the smaller bounding-box containing both `self` and
84    /// `other`.
85    #[inline]
86    pub fn hull(&self, other: &Self) -> Self {
87        BoundingBox { xmin: self.xmin.min(other.xmin),
88                      xmax: self.xmax.max(other.xmax),
89                      ymin: self.ymin.min(other.ymin),
90                      ymax: self.ymax.max(other.ymax) }
91    }
92}
93
94
95////////////////////////////////////////////////////////////////////////
96//
97// Sampling datastructure
98
99/// A 2D point with coordinates (`x`, `y`) supposed to be given by a
100/// function evaluated at "time" `t`.  A path is a sequence of points.
101/// Points that are invalid (see [`Point::is_valid`]) are considered
102/// cuts in the path (the line is interrupted).  As most 2 cuts may
103/// follow each other (to track ranges of t outside the domain of the
104/// function) except at the boundary of the path where at most 1 cut
105/// is allowed.
106#[derive(Debug)]
107struct Point<D> {
108    t: f64, // "time" parameter, ALWAYS finite.
109    xy: [f64; 2],
110    data: D,
111    cost: f64, // Cache the cost of the point (not the segment).  If
112               // the point is not valid, the cost has no meaning.
113
114    // Pointer to the priority queue node if the point is the initial
115    // point of a segment.  `None` otherwise.
116    witness: RefCell<Option<pq::Witness<list::Witness<Point<D>>>>>,
117}
118
119/// Essential elements of a point for the algorithm.  This is to pass
120/// to functions to work around the fact that `Point` is not copy
121/// (when we borrow a point, we implicitly borrow the whole path).
122//
123// It is actually slower to use `Coord` in `Point`.
124#[derive(Clone, Copy, Debug)]
125struct Coord {
126    t: f64,
127    xy: [f64; 2],
128    cost: f64,
129}
130
131impl<D: Clone> Clone for Point<D> {
132    // As a cloned point may likely go to another path, reset the
133    // priority queue pointer.
134    fn clone(&self) -> Self {
135        Self {
136            t: self.t,  xy: self.xy,
137            data: self.data.clone(),
138            cost: self.cost,
139            witness: None.into(),
140        }
141    }
142}
143
144impl<D> Point<D> {
145    /// Return a new point.  `t` is assumed to be finite.
146    #[inline]
147    fn new_unchecked(t: f64, xy: [f64; 2], data: D) -> Self {
148        Point { t, xy, data,  cost: 0.,
149            witness: None.into() }
150    }
151
152    /// Return a point considered to be a cut in the path.
153    #[inline]
154    fn cut(t: f64, data: D) -> Self {
155        Point { t, xy: [f64::NAN; 2], data,
156                cost: 0.,
157            witness: None.into() }
158    }
159
160    /// Return `true` if the point is valid — otherwise it is a cut.
161    #[inline]
162    fn is_valid(&self) -> bool {
163        self.xy.iter().all(|z| z.is_finite())
164    }
165
166    fn cmp_t_incr(p1: &Self, p2: &Self) -> Ordering {
167        p1.t.total_cmp(&p2.t)
168    }
169
170    fn cmp_t_decr(p1: &Self, p2: &Self) -> Ordering {
171        p1.t.total_cmp(&p2.t).reverse()
172    }
173
174    /// Transform the data as optional.
175    #[inline]
176    fn opt(self) -> Point<Option<D>> {
177        Point {
178            t: self.t,  xy: self.xy,
179            data: Some(self.data),
180            cost: self.cost,
181            witness: None.into(),
182        }
183    }
184
185    /// Project on the essential information for cost computation.
186    ///
187    /// This is used to work around the fact that Point<_> can not be
188    /// copy because <T> and the `witness`.
189    #[inline]
190    fn coord(&self) -> Coord {
191        Coord { t: self.t, xy: self.xy, cost: self.cost }
192    }
193}
194
195/// A sampling of a 2D curve.  This can be thought of as a 2D path,
196/// with possible "cuts" due to discontinuities or leaving the domain
197/// of the (parametric) function describing the path.
198///
199/// Additional data of type `D` may be attached to each point.
200pub struct Sampling<D> {
201    // Priority queue of segments.  The value points to the first
202    // `Point` of the segment (thus its .next pointer must not be
203    // null).  At least one of the endpoint of all segments must be
204    // valid (see `Point::is_valid`) If `pq` is empty but `begin` is
205    // not null, it means that the costs need to to be updated.
206    pq: PQ<D>,
207    path: List<Point<D>>,
208    vp: Option<BoundingBox>, // viewport (zone of interest)
209}
210
211type PQ<D> = pq::PQ<list::Witness<Point<D>>>;
212
213impl<D: Clone> Clone for Sampling<D> {
214    fn clone(&self) -> Self {
215        // It is guessed that one clones the sampling to transform it.
216        // Thus start with an empty queue.
217        Self { pq: PQ::new(),
218               path: self.path.clone(),
219               vp: self.vp }
220    }
221}
222
223/// `t` is the length of total range of time, `x` and `y` are the
224/// dimensions of the bounding box.
225#[derive(Debug, Clone, Copy)]
226struct Lengths {
227    t: f64,
228    x: f64,
229    y: f64,
230}
231
232impl<D> Sampling<D> {
233    /// Return `true` if the sampling contains no point.
234    #[inline]
235    pub fn is_empty(&self) -> bool { self.path.is_empty() }
236
237    /// Create an empty sampling.
238    #[inline]
239    pub(crate) fn empty() -> Self {
240        Self { pq: PQ::new(),
241               path: List::new(),
242               vp: None }
243    }
244
245    #[inline]
246    pub(crate) fn singleton(p: Point<D>) -> Self {
247        debug_assert!(p.t.is_finite() && p.xy.iter().all(|z| z.is_finite()));
248        let mut path = List::new();
249        path.push_back(p);
250        Self { pq: PQ::new(), path, vp: None }
251    }
252
253    fn from_list(path: List<Point<D>>) -> Self {
254        Self {
255            pq: PQ::new(),
256            path,
257            vp: None,
258        }
259    }
260
261    #[inline]
262    pub(crate) fn set_vp(&mut self, bb: BoundingBox) {
263        self.vp = Some(bb);
264    }
265
266    /// Return the length of the "time interval" as well as the
267    /// lengths of the viewport.
268    pub(crate) fn lengths(&self) -> Option<Lengths> {
269        if self.is_empty() { return None }
270        let p0 = self.path.first().unwrap();
271        let p1 = self.path.last().unwrap();
272        let (len_x, len_y) = match self.vp {
273            Some(vp) => (vp.xmax - vp.xmin, vp.ymax - vp.ymin),
274            None => (1., 1.),
275
276        };
277        Some(Lengths { t: (p1.t - p0.t).abs(), x: len_x, y: len_y })
278    }
279
280    /// Return the number of points and "cuts" in the sampling.
281    pub fn len(&self) -> usize {
282        self.path.len()
283    }
284
285    /// Returns an iterator on the points (and cuts) of the path.
286    /// More precisely, a path is made of continuous segments whose
287    /// points are given by contiguous values `[x,y]` with both `x`
288    /// and `y` not NaN, interspaced by "cuts" `[f64::NAN; 2]`.  Two
289    /// cuts never follow each other.  Isolated points `p` are given
290    /// by ... `[f64::NAN; 2]`, `p`, `None`,...
291    pub fn iter(&self) -> Iter<'_, D> {
292        Iter { iter: self.iter_data() }
293    }
294
295    /// Same as [`Self::iter`] but also provides a reference to the
296    /// data associated to each point.
297    pub fn iter_data(&self) -> IterData<'_, D> {
298        IterData {
299            path: self.path.iter(),
300            prev_is_cut: true,
301        }
302    }
303
304    /// Returns an iterator that allows to modify the points and cuts
305    /// of the path.  Unlike [`Self::iter`], this iterates on all the
306    /// nodes even if several cuts (i.e., node with a non finite
307    /// coordinate) follow each other.  The data associated to each
308    /// point is unmodified.
309    pub fn iter_mut(&mut self) -> IterMut<'_, D> {
310        IterMut { path: self.path.iter_mut() }
311    }
312
313    /// Consumes the sampling and return an iterator on the curve
314    /// points and (owned) associated data.
315    pub fn into_iter_data(self) -> IntoIterData<D> {
316        IntoIterData { path: self.path.into_iter() }
317    }
318
319    /// Return the X-coordinates of the sampling.
320    #[inline]
321    pub fn x(&self) -> Vec<f64> {
322        let mut v = Vec::with_capacity(self.path.len());
323        for [x, _] in self.iter() { v.push(x) }
324        v
325    }
326
327    /// Return the Y-coordinates of the sampling.
328    #[inline]
329    pub fn y(&self) -> Vec<f64> {
330        let mut v = Vec::with_capacity(self.path.len());
331        for [_, y] in self.iter() { v.push(y) }
332        v
333    }
334}
335
336
337/// Iterator on the curve points (and cuts) together with the attached data.
338///
339/// Created by [`Sampling::iter_data`].
340pub struct IterData<'a, D> {
341    path: list::Iter<'a, Point<D>>,
342    prev_is_cut: bool,
343}
344
345impl<'a, D> Iterator for IterData<'a, D> {
346    type Item = ([f64; 2], &'a D);
347
348    fn next(&mut self) -> Option<Self::Item> {
349        match self.path.next() {
350            None => None,
351            Some(p) => {
352                if p.is_valid() {
353                    self.prev_is_cut = false;
354                    Some((p.xy, &p.data))
355                } else if self.prev_is_cut {
356                    // Find the next valid point.
357                    // FIXME: use `try_find` once stabilized.
358                    let r = self.path.try_fold((), |_, p| {
359                        if p.is_valid() {
360                            ControlFlow::Break(((), p))
361                        } else {
362                            ControlFlow::Continue(())
363                        }
364                    });
365                    match r {
366                        ControlFlow::Continue(_) => {
367                            // Iterator exhausted
368                            None
369                        }
370                        ControlFlow::Break((_, p)) => {
371                            self.prev_is_cut = false;
372                            Some((p.xy, &p.data))
373                        }
374                    }
375                } else {
376                    self.prev_is_cut = true;
377                    Some(([f64::NAN; 2], &p.data))
378                }
379            }
380        }
381    }
382
383    fn size_hint(&self) -> (usize, Option<usize>) {
384        (0, Some(self.path.len()))
385    }
386}
387
388/// Iterator on the curve points (and cuts).
389///
390/// Created by [`Sampling::iter`].
391pub struct Iter<'a, D> {
392    iter: IterData<'a, D>,
393}
394
395impl<'a, D> Iterator for Iter<'a, D> {
396    type Item = [f64; 2];
397
398    #[inline]
399    fn next(&mut self) -> Option<Self::Item> {
400        self.iter.next().map(|(xy, _)| xy)
401    }
402
403    fn size_hint(&self) -> (usize, Option<usize>) {
404        self.iter.size_hint()
405    }
406}
407
408
409/// Mutable iterator on the curve points (and cuts).
410///
411/// Created by [`Sampling::iter_mut`].
412pub struct IterMut<'a, D> {
413    path: list::IterMut<'a, Point<D>>,
414}
415
416impl<'a, D> Iterator for IterMut<'a, D> {
417    type Item = &'a mut [f64; 2];
418
419    fn next(&mut self) -> Option<Self::Item> {
420        self.path.next().map(|p| &mut p.xy)
421    }
422
423    fn size_hint(&self) -> (usize, Option<usize>) {
424        (self.path.len(), Some(self.path.len()))
425    }
426}
427
428/// Iterator returning the curve points and owned data.
429///
430/// Created by [`Sampling::into_iter_data`].
431pub struct IntoIterData<D> {
432    path: list::IntoIter<Point<D>>,
433}
434
435impl<D> Iterator for IntoIterData<D> {
436    type Item = ([f64; 2], D);
437
438    fn next(&mut self) -> Option<Self::Item> {
439         self.path.next().map(|p| (p.xy, p.data))
440    }
441
442    fn size_hint(&self) -> (usize, Option<usize>) {
443        (self.path.len(), Some(self.path.len()))
444    }
445}
446
447
448/// Intersection of a segment with the bounding box.
449#[derive(Debug)]
450enum Intersection<D> {
451    Empty,
452    Pt(Point<D>),
453    Seg(Point<D>, Point<D>),
454}
455
456impl<D> Sampling<D> {
457    /// Return the smallest rectangle enclosing all the points of the
458    /// sampling `self`.  If the path is empty, the "min" fields of
459    /// the bounding box are set to +∞ and "max" fields to -∞.
460    ///
461    /// # Examples
462    ///
463    /// ```
464    /// use curve_sampling::{Sampling, BoundingBox};
465    /// let s = Sampling::from_iter([3., 4.]);
466    /// let bb = BoundingBox { xmin: 0., xmax: 1., ymin: 3., ymax: 4. };
467    /// assert_eq!(s.bounding_box(), bb);
468    /// ```
469    ///
470    /// ```
471    /// use curve_sampling::Sampling;
472    /// let s = Sampling::from_iter([0.]);
473    /// assert!(s.bounding_box().is_empty());
474    /// ```
475    pub fn bounding_box(&self) -> BoundingBox {
476        let mut points = self.iter().skip_while(|[x,_]| x.is_nan());
477        let mut bb = match &points.next() {
478            Some([x, y]) => BoundingBox { xmin: *x,  xmax: *x,
479                                         ymin: *y,  ymax: *y },
480            None => return BoundingBox::empty()
481        };
482        for [x, y] in points {
483            if x < bb.xmin { bb.xmin = x }
484            else if bb.xmax < x { bb.xmax = x };
485            if y < bb.ymin { bb.ymin = y }
486            else if bb.ymax < y { bb.ymax = y };
487        }
488        bb
489    }
490
491    /// Transpose in place the x and y coordinates of the sampling.
492    pub fn transpose(&mut self) -> &mut Self {
493        for p in self.path.iter_mut() {
494            let [x, y] = p.xy;
495            p.xy = [y, x];
496        }
497        self
498    }
499
500    /// Assume `p0` ∈ `bb` and `p1` ∉ `bb`.  Return the point
501    /// intersecting the boundary of `bb`.  If the intersection point
502    /// is the same as `p0`, return `None`
503    #[inline]
504    #[must_use]
505    fn intersect(
506        p0: Coord, p1: Coord, bb: BoundingBox
507    ) -> Option<Point<Option<D>>> {
508        let [x0, y0] = p0.xy;
509        let [x1, y1] = p1.xy;
510        let mut t = 1.; // t ∈ [0, 1]
511        let dx = x1 - x0; // May be 0.
512        let r = (if dx >= 0. {bb.xmax} else {bb.xmin} - x0) / dx;
513        if r < t { t = r } // ⟹ r finite (as r ≥ 0 or NaN)
514        let dy = y1 - y0; // May be 0.
515        let r = (if dy >= 0. {bb.ymax} else {bb.ymin} - y0) / dy;
516        if r < t { t = r };
517        if t <= 1e-14 {
518            None
519        } else {
520            Some(Point::new_unchecked(
521                p0.t + t * (p1.t - p0.t),
522                [x0 + t * dx, y0 + t * dy],
523            None))
524        }
525    }
526
527    /// Assume `p0` ∉ `bb` and `p1` ∉ `bb` (thus, not even on the
528    /// boundary of `bb`) and `p0` ≠ `p1`.  Return the endpoints of
529    /// the segment intersecting the boundary of `bb` if any.  The
530    /// "parameter direction" of `p0`, `p1` is preserved.
531    #[inline]
532    #[must_use]
533    fn intersect_seg(
534        p0: Coord, p1: Coord, bb: BoundingBox,
535    ) -> Intersection<Option<D>> {
536        let [x0, y0] = p0.xy;
537        let [x1, y1] = p1.xy;
538        let mut t0 = 0.; // t0 ∈ [0, 1]
539        let mut t1 = 1.; // t1 ∈ [0, 1]
540        let dx = x1 - x0; // may be 0.
541        // r0 ≤ r1 or NAN if on x-boundary lines.
542        let (r0, r1) = if dx >= 0. {
543            ((bb.xmin - x0) / dx, (bb.xmax - x0) / dx)
544        } else {
545            ((bb.xmax - x0) / dx, (bb.xmin - x0) / dx)
546        };
547        if r0 > 1. || r1 < 0. { return Intersection::Empty }
548        if r0 > 0. { t0 = r0 } // if r0 is NAN, keep the whole segment
549        if r1 < 1. { t1 = r1 }
550        let dy = y1 - y0; // may be 0.
551        let (r0, r1) = if dy >= 0. {
552            ((bb.ymin - y0) / dy, (bb.ymax - y0) / dy)
553        } else {
554            ((bb.ymax - y0) / dy, (bb.ymin - y0) / dy)
555        };
556        if r0 > t1 || r1 < t0 { return Intersection::Empty }
557        if r0 > t0 { t0 = r0 }
558        if r1 < t1 { t1 = r1 }
559        if t0 < t1 { // segment not reduced to a point
560            let dt = p1.t - p0.t;
561            let q0 = Point::new_unchecked(p0.t + t0 * dt,
562                [x0 + t0 * dx, y0 + t0 * dy], None);
563            let q1 = Point::new_unchecked(p0.t + t1 * dt,
564                [x0 + t1 * dx, y0 + t1 * dy], None);
565            Intersection::Seg(q0, q1)
566        } else if t0 == t1 {
567            let q0 = Point::new_unchecked(
568                p0.t + t0 * (p1.t - p0.t),
569                [x0 + t0 * dx, y0 + t0 * dy], None);
570            Intersection::Pt(q0)
571        } else {
572            Intersection::Empty
573        }
574    }
575
576    /// Returns the sampling `self` but clipped to the 2D box `bb`.  A
577    /// path that crosses the boundary will get additional nodes at
578    /// the points of crossing and the part outside the bounding box
579    /// will be dropped.  (Thus a path entirely out of the bounding
580    /// box will be removed.)
581    #[must_use]
582    pub fn clip(self, bb: BoundingBox) -> Sampling<Option<D>> {
583        let mut path = Vec::new();
584        // First point of the current segment, if any.
585        let mut p0_opt: Option<Coord> = None;
586        let mut p0_inside = false;
587        let mut prev_cut = true; // New path ("virtual" cut)
588        for p1 in self.path.into_iter() {
589            if prev_cut {
590                // A cut was pushed at an earlier step (and the path
591                // has not subsequently intersected `bb`).  This may
592                // be because the original path was just cut (`p0_opt
593                // = None`) or because the previous segment was cut
594                // (`p0_opt = Some(p0)` with `p0` ∉ `bb`) or because
595                // there was an earlier cut and the next point `p0` is
596                // the possible new start.
597                match (p0_opt, p1.is_valid()) {
598                    (Some(p0), true) => {
599                        let p1_inside = bb.contains(&p1);
600                        p0_opt = Some(p1.coord());
601                        if p0_inside { // p0 ∈ bb with cut before
602                            // p0 is already in `s`.
603                            if p1_inside {
604                                path.push(p1.opt());
605                                prev_cut = false;
606                            } else if
607                                let Some(p) = Self::intersect(p0, p1.coord(), bb) {
608                                    let t = p.t;
609                                    path.push(p);
610                                    path.push(Point::cut(t, None));
611                                } else {
612                                    let c = Point::cut(p0.t, None);
613                                    path.push(c);
614                                }
615                        } else if p1_inside { // p0 ∉ bb, p1 ∈ bb
616                            if let Some(p) = Self::intersect(p1.coord(), p0, bb) {
617                                path.push(p); // p ≠ p1
618                            }
619                            path.push(p1.opt());
620                            prev_cut = false;
621                        } else { // p0, p1 ∉ bb but maybe intersection
622                            match Self::intersect_seg(p0, p1.coord(), bb) {
623                                Intersection::Seg(q0, q1) => {
624                                    let t1 = q1.t;
625                                    path.push(q0);
626                                    path.push(q1);
627                                    path.push(Point::cut(t1, None));
628                                }
629                                Intersection::Pt(p) => {
630                                    let t = p.t;
631                                    path.push(p);
632                                    path.push(Point::cut(t, None));
633                                }
634                                Intersection::Empty => (),
635                            }
636                        }
637                        p0_inside = p1_inside;
638                    }
639                    (None, true) => {
640                        p0_inside = bb.contains(&p1);
641                        p0_opt = Some(p1.coord());
642                        if p0_inside {
643                            // We keep `p0`.
644                            path.push(p1.opt());
645                        }
646                    }
647                    (Some(p0), false) => {
648                        if p0_inside {
649                            // `p0` is already in the `s.path`.
650                            path.push(Point::cut(p0.t, None));
651                        }
652                        p0_opt = None;
653                    }
654                    (None, false) => (), // p0_opt == None
655                }
656            } else {
657                // Previous step was not a cut which also implies that
658                // `p0_opt = Some(p0)` with `p0` ∈ `bb`, and `p0` is
659                // already present in the final sampling.
660                let p0 = p0_opt.expect("No cut ⟹ previous point");
661                debug_assert!(p0_inside);
662                if p1.is_valid() {
663                    p0_opt = Some(p1.coord());
664                    p0_inside = bb.contains(&p1); // update for next step
665                    if p0_inside { // p0, p1 ∈ bb
666                        path.push(p1.opt());
667                    } else { // p0 ∈ bb, p1 ∉ bb
668                        if let Some(p) = Self::intersect(p0, p1.coord(), bb) {
669                            let t = p.t;
670                            path.push(p);
671                            path.push(Point::cut(t, None));
672                        } else {
673                            path.push(Point::cut(p0.t, None));
674                        }
675                        prev_cut = true;
676                    }
677                } else { // p1 is invalid (i.e., represent a cut)
678                    p0_opt = None;
679                    path.push(p1.opt());
680                    prev_cut = true
681                }
682            }
683        }
684        if prev_cut { path.pop(); }
685        let mut s = Sampling::from_list(path.into());
686        s.set_vp(bb);
687        s
688    }
689
690    /// Create a sampling from an iterator of points.
691    ///
692    /// # Preconditions
693    /// - It is assumed that the `Point.t` field is sorted in increasing
694    ///   or decreasing order.
695    /// - The invariant "`p.y` is finite ⇒ `p.x` is finite" is assumed.
696    fn from_point_iterator<P>(points: P) -> Self
697    where P: IntoIterator<Item = Point<D>> {
698        let mut s = Sampling::empty();
699        let mut points = points.into_iter();
700        macro_rules! skip_until_last_cut { () => {
701            let mut cut = None;
702            let mut first_pt = None;
703            while let Some(p) = points.next() {
704                if p.is_valid() { first_pt = Some(p); break; }
705                cut = Some(p);
706            }
707            match (cut, first_pt) {
708                (_, None) => {
709                    return s
710                }
711                (None, Some(p)) => {
712                    s.path.push_back(p);
713                }
714                (Some(c), Some(p)) => {
715                    s.path.push_back(Point::cut(c.t, c.data));
716                    s.path.push_back(p);
717                }
718            }
719        }}
720        skip_until_last_cut!();
721        while let Some(p) = points.next() {
722            if p.is_valid() {
723                s.path.push_back(p);
724            } else {
725                s.path.push_back(Point::cut(p.t, p.data));
726                skip_until_last_cut!();
727            }
728        }
729        s
730    }
731}
732
733impl<D, Y> FromIterator<Y> for Sampling<D>
734where Y: Img<D> {
735    /// Return an sampling from the Y-values (possibly with data).
736    /// The X-values are the indices of the elements (see
737    /// [`Iterator::enumerate`]).  Points with non-finite coordinates
738    /// are interpreted as cuts.
739    fn from_iter<P>(points: P) -> Self
740    where P: IntoIterator<Item = Y> {
741        Sampling::from_point_iterator(
742            points.into_iter().enumerate().map(|(i, y)| {
743                let t = i as f64;
744                let p = y.into_point(t);
745                let [x, y] = p.xy;
746                if x.is_finite() && y.is_finite() {
747                    p
748                } else {
749                    Point::cut(t, p.data)
750                } }))
751    }
752}
753
754struct MergeSortedIterator<I1, I2, CMP>
755where I1: Iterator,
756      I2: Iterator,
757{
758    i1: std::iter::Peekable<I1>,
759    i2: std::iter::Peekable<I2>,
760    cmp: CMP,
761}
762
763impl<I1, I2, CMP> MergeSortedIterator<I1, I2, CMP>
764where I1: Iterator,
765      I2: Iterator,
766      CMP: Fn(&I1::Item, &I2::Item) -> Ordering,
767{
768    /// Create a new iterator returning the values of `i1` and `i2` in
769    /// the order given by `cmp`.
770    ///
771    /// # Precondition
772    /// The iterators `i1` and `i2` are assumed to be sorted, both
773    /// with the order `cmp`.
774    fn new(i1: I1, i2: I2, cmp: CMP) -> Self {
775        Self {
776            i1: i1.peekable(),
777            i2: i2.peekable(),
778            cmp,
779        }
780    }
781}
782
783impl<I1, I2, CMP> Iterator for MergeSortedIterator<I1, I2, CMP>
784where I1: Iterator,
785      I2: Iterator<Item = I1::Item>,
786      CMP: Fn(&I1::Item, &I1::Item) -> Ordering,
787{
788    type Item = I1::Item;
789
790    fn next(&mut self) -> Option<Self::Item> {
791        if let Some(p1) = self.i1.peek() {
792            if let Some(p2) = self.i2.peek() {
793                match (self.cmp)(p1, p2) {
794                    Ordering::Less => self.i1.next(),
795                    Ordering::Greater => self.i2.next(),
796                    Ordering::Equal => {
797                        // For a given time, the data should be equal.
798                        drop(self.i2.next());
799                        self.i1.next()
800                    },
801                }
802            } else {
803                self.i1.next()
804            }
805        } else {
806            self.i2.next()
807        }
808    }
809}
810
811////////////////////////////////////////////////////////////////////////
812//
813// Acceptable return values to generate samplings, possibly with
814// attached data.
815
816/// Suitable images of functions that can be used to generate samplings.
817///
818/// See e.g. [`Sampling::uniform`] and [`Sampling::fun`].
819//
820// This trait is not implementable from the outside so it can uphold
821// `Point` invariants for example `Point.t` must be the `t` provided
822// and be finite,...
823pub trait Img<D> {
824    /// Transform a map `f64` → `self` into the internal value `Point`.
825    /// It will only be used with finite `t`.
826    #[allow(private_interfaces)]
827    fn into_point(self, t: f64) -> Point<D>;
828}
829
830/// Type indicating that no data is associated with the sampling.
831#[derive(Clone, Debug)]
832pub struct NoData {
833    // Non-constructible outside this library (i.e. can only be used
834    // as a type annotation).
835    marker: PhantomData<()>,
836}
837
838impl NoData {
839    fn new() -> Self { Self { marker: PhantomData } }
840}
841
842impl Img<NoData> for f64 {
843    #[allow(private_interfaces)]
844    #[inline]
845    fn into_point(self, t: f64) -> Point<NoData> {
846        Point::new_unchecked(t, [t, self], NoData::new())
847    }
848}
849
850impl Img<NoData> for [f64; 2] {
851    #[allow(private_interfaces)]
852    #[inline]
853    fn into_point(self, t: f64) -> Point<NoData> {
854        Point::new_unchecked(t, self, NoData::new())
855    }
856}
857
858impl Img<NoData> for (f64, f64) {
859    #[allow(private_interfaces)]
860    #[inline]
861    fn into_point(self, t: f64) -> Point<NoData> {
862        Point::new_unchecked(t, [self.0, self.1], NoData::new())
863    }
864}
865
866#[cfg(feature = "num-complex")]
867impl Img<num_complex::Complex<f64>> for num_complex::Complex<f64> {
868    #[allow(private_interfaces)]
869    #[inline]
870    fn into_point(self, t: f64) -> Point<num_complex::Complex<f64>> {
871        Point::new_unchecked(t, [self.re, self.im], self)
872    }
873}
874
875/// Wrapper to indicate that a value is a data associated with a point.
876///
877/// See [`Sampling::fun`] for an example.
878pub struct Data<D>(pub D);
879
880/// Values `(y, Data(d))` are acceptable images (see [`Sampling::fun`]).
881impl<D> Img<D> for (f64, Data::<D>) {
882    #[allow(private_interfaces)]
883    #[inline]
884    fn into_point(self, t: f64) -> Point<D> {
885        Point::new_unchecked(t, [t, self.0], self.1.0)
886    }
887}
888
889/// Values `([x,y], Data(d))` are acceptable images (see [`Sampling::fun`]).
890impl<D> Img<D> for ([f64; 2], Data::<D>) {
891    #[allow(private_interfaces)]
892    #[inline]
893    fn into_point(self, t: f64) -> Point<D> {
894        Point::new_unchecked(t, self.0, self.1.0)
895    }
896}
897
898#[cfg(feature = "num-complex")]
899impl<D> Img<D> for (num_complex::Complex<f64>, Data::<D>) {
900    #[allow(private_interfaces)]
901    #[inline]
902    fn into_point(self, t: f64) -> Point<D> {
903        Point::new_unchecked(t, [self.0.re, self.0.im], self.1.0)
904    }
905}
906
907
908////////////////////////////////////////////////////////////////////////
909//
910// Defining a sampling with standard options & checks
911
912/// Define a structure with standard fields, standard options, and a
913/// function to generate it.
914macro_rules! new_sampling_fn {
915    // Function to init the struct.
916    ($(#[$docfn: meta])*, $(#[$docfn_extra: meta])* $fun: ident -> $ft: ty,
917     // The structure to hold the options (and other fields).
918     $(#[$doc: meta])* $struct: ident,
919     $wrap_f: ident
920    ) => {
921        impl<D> Sampling<D> {
922            $(#[$docfn])*
923            ///
924            /// Panics if `a` or `b` is not finite.
925            ///
926            $(#[$docfn_extra])*
927            #[must_use]
928            #[allow(deprecated)]
929            pub fn $fun<F, Y>(f: F, a: f64, b: f64) -> $struct<F, D>
930            where F: FnMut(f64) -> Y,
931                  Y: Img<D> {
932                if !a.is_finite() {
933                    panic!("curve_sampling::{}: a = {} must be finite",
934                           stringify!($fun), a);
935                }
936                if !b.is_finite() {
937                    panic!("curve_sampling::{}: b = {} must be finite",
938                           stringify!($fun), b);
939                }
940                $struct { f,
941                          a, b,  // Order of `a`, `b` reflect orientation
942                          n: 100,
943                          viewport: None,
944                          init: vec![],
945                          init_pt: vec![],
946                }
947            }
948        }
949
950        $(#[$doc])*
951        pub struct $struct<F, D> {
952            f: F,  a: f64,  b: f64,
953            n: usize,
954            viewport: Option<BoundingBox>,
955            init: Vec<f64>,
956            init_pt: Vec<Point<D>>,
957        }
958
959        #[allow(deprecated)]
960        impl<F, Y, D> $struct<F, D>
961        where F: FnMut(f64) -> Y,
962              Y: Img<D>,
963        {
964            /// Set the maximum number of evaluations of the function
965            /// to build the sampling.  Panic if `n < 2`.  An initial
966            /// exploration of the function always uses at least 10
967            /// evaluations so, for `n < 10`, this is the number of
968            /// evaluations performed.
969            pub fn n(mut self, n: usize) -> Self {
970                if n < 2 {
971                    panic!("curve_sampling: n = {} must at least be 2", n)
972                }
973                self.n = n;
974                self
975            }
976
977            /// Set the zone of interest for the sampling.  Segments
978            /// that end up outside this box will not be refined.
979            pub fn viewport(mut self, vp: BoundingBox) -> Self {
980                self.viewport = Some(vp);
981                self
982            }
983
984            /// Add initial values of `t` such that `f(t)` (see [`
985            #[doc = stringify!(Sampling::$fun)]
986            /// `]) must be included into the sampling in addition to
987            /// the `n` evaluations.  Only the values between `a` and
988            /// `b` are taken into account (other values are ignored).
989            pub fn init<'a, I>(mut self, ts: I) -> Self
990            where I: IntoIterator<Item = &'a f64> {
991                for &t in ts {
992                    if self.a <= t && t <= self.b { // ⟹ t is finite
993                        self.init.push(t);
994                    }
995                }
996                self
997            }
998
999            /// Add initial points `(t, f(t))` to include into the
1000            /// sampling in addition to the `n` evaluations.  This
1001            /// allows you to use previous evaluations of `f`.  Only
1002            /// the couples with first coordinate `t` between `a` and
1003            /// `b` (see [`
1004            #[doc = stringify!(Sampling::$fun)]
1005            /// `]) are considered (other values are ignored).
1006            pub fn init_pt<I>(mut self, pts: I) -> Self
1007            where I: IntoIterator<Item = (f64, Y)>,
1008            {
1009                for (t, y) in pts {
1010                    if self.a <= t && t <= self.b { // ⟹ p.0 = t is finite
1011                        self.init_pt.push(y.into_point(t));
1012                    }
1013                }
1014                self
1015            }
1016
1017            /// Evaluate the function at all initial values that where
1018            /// provided through [`Self::init`] and add the results to
1019            /// [`self.init_pt`].  Moreover sort all points by
1020            /// increasing or decreasing order of `Point`s `t`
1021            /// according to the order of `self.a` and `self.b`.
1022            fn eval_init_and_sort(&mut self) {
1023                self.init_pt.reserve(self.init.len());
1024                // `t` ∈ \[`a`, `b`\] already checked by [`init`] and
1025                // [`init_pt`].
1026                for &t in &self.init {
1027                    self.init_pt.push((self.f)(t).into_point(t))
1028                }
1029                if self.a <= self.b {
1030                    self.init_pt.sort_unstable_by(Point::cmp_t_incr);
1031                } else {
1032                    self.init_pt.sort_unstable_by(Point::cmp_t_decr);
1033                }
1034            }
1035        }
1036    }
1037}
1038
1039////////////////////////////////////////////////////////////////////////
1040//
1041// Uniform sampling
1042
1043new_sampling_fn!(
1044    /// Create a sampling for the graph of `f` on the interval
1045    /// \[`a`, `b`\] with evenly spaced values of the argument.
1046    ///
1047    /// See [`Sampling::fun`] for more documentation on the possible
1048    /// return values of `f`.
1049    ,
1050    /// # Example
1051    ///
1052    /// ```
1053    /// use std::{fs::File, io::BufWriter};
1054    /// use curve_sampling::Sampling;
1055    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1056    /// let s = Sampling::uniform(|x| x.sin(), 0., 4.).build();
1057    /// s.write(&mut BufWriter::new(File::create("target/uniform.dat")?))?;
1058    /// # Ok(()) }
1059    /// ```
1060    uniform -> f64,
1061    /// Options for uniform sampling graphs of function ℝ → ℝ.
1062    /// See [`Sampling::uniform`].
1063    Uniform,
1064    FnPoint);
1065
1066impl<F, Y, D> Uniform<F, D>
1067where F: FnMut(f64) -> Y,
1068      Y: Img<D> {
1069    /// Return a uniform sampling of the function.
1070    pub fn build(mut self) -> Sampling<D> {
1071        if self.a == self.b {
1072            // `a` is finite by previous tests
1073            let p = (self.f)(self.a).into_point(self.a);
1074            if p.is_valid() {
1075                return Sampling::singleton(p);
1076            } else {
1077                return Sampling::empty()
1078            }
1079        }
1080        self.eval_init_and_sort();
1081        let dt = (self.b - self.a) / (self.n - 1) as f64;
1082        let uniform = (0 .. self.n).into_iter().map(|i| {
1083            let t = self.a + i as f64 * dt;
1084            (self.f)(t).into_point(t)
1085        });
1086        let cmp = if self.a <= self.b {
1087            Point::cmp_t_incr
1088        } else {
1089            Point::cmp_t_decr
1090        };
1091        let points = MergeSortedIterator::new(
1092            self.init_pt.into_iter(),
1093            uniform,
1094            cmp);
1095        Sampling::from_point_iterator(points)
1096    }
1097}
1098
1099////////////////////////////////////////////////////////////////////////
1100//
1101// Cost
1102
1103mod cost {
1104    use super::{Point, Coord, Lengths};
1105
1106    // The cost of a point is a measure of the curvature at this
1107    // point.  This requires segments before and after the point.  In
1108    // case the point is a cut, or first, or last, it has a cost of 0.
1109    // If it is an endpoint of a segment with the other point a cut,
1110    // the cost is set to [`HANGING_NODE`] because the segment with
1111    // the invalid point needs to be cut of too long to better
1112    // determine the boundary.  Only the absolute value of the cost
1113    // matters for the priority queue (see `segment`), its sign must
1114    // reflect the orientation of the angle.
1115    //
1116    // The cost of a point is apportioned to the segments of which it is
1117    // an endpoint according to their relative lengths.  More precisely,
1118    // the cost c of a point p is distributed on the segments s1 and s2
1119    // (of respective lengths l1 and l2) it is an endpoint of as
1120    //
1121    //   c * l1/(l1+l2) for s1 and c * l2/(l1+l2) for s2.
1122    //
1123    // In order to be able to update the cost of s1 without accessing
1124    // s2, p.cost holds c/(l1+l2).
1125
1126    /// Cost for new "hanging" nodes — nodes created splitting a
1127    /// segment with an invalid endpoint.  Note that this cost will be
1128    /// multiplied by a function of `dt` in `segment` so it must be
1129    /// set high enough to ensure proper resolution of the endpoints
1130    /// of the domain.
1131    pub const HANGING_NODE: f64 = 5e5;
1132
1133    /// Set the cost of the middle point `pm`.  Assumes `pm` is a
1134    /// valid point.
1135    #[inline]
1136    pub(crate) fn set_middle<D>(
1137        p0: Coord, pm: &mut Point<D>, p1: Coord, len: Lengths
1138    ) {
1139        let [x0, y0] = p0.xy;
1140        let [xm, ym] = pm.xy;
1141        let [x1, y1] = p1.xy;
1142        let dx0m = (x0 - xm) / len.x;
1143        let dy0m = (y0 - ym) / len.y;
1144        let dx1m = (x1 - xm) / len.x;
1145        let dy1m = (y1 - ym) / len.y;
1146        let len0m = dx0m.hypot(dy0m);
1147        let len1m = dx1m.hypot(dy1m);
1148        if len0m == 0. || len1m == 0. {
1149            pm.cost = 0.; // Do not subdivide
1150        } else {
1151            let dx = - dx0m * dx1m - dy0m * dy1m;
1152            let dy = dy0m * dx1m - dx0m * dy1m;
1153            pm.cost = dy.atan2(dx); // ∈ [-π, π]
1154            debug_assert!(!pm.cost.is_nan());
1155        }
1156    }
1157
1158    /// Compute the cost of a segment according to the costs of its
1159    /// endpoints unless `in_vp` is false in which case -∞ is returned.
1160    #[inline]
1161    pub(crate) fn segment_vp(
1162        p0: Coord,
1163        p1: Coord,
1164        len: Lengths,
1165        in_vp: bool
1166    ) -> f64 {
1167        if ! in_vp { return f64::NEG_INFINITY }
1168        segment(p0, p1, len)
1169    }
1170
1171    /// Compute the cost of a segment according to the costs of its
1172    /// endpoints.
1173    #[inline]
1174    pub(crate) fn segment(p0: Coord, p1: Coord, len: Lengths) -> f64 {
1175        let dt = (p1.t - p0.t).abs() / len.t; // ∈ [0,1]
1176        debug_assert!((0. ..=1.).contains(&dt), "dt = {dt} ∉ [0,1]");
1177        // Put less efforts when `dt` is small.  For functions, the
1178        // Y-variation may be large but, if it happens for a small range
1179        // of `t`, there is no point in adding indistinguishable details.
1180        let [x0, y0] = p0.xy;
1181        let [x1, y1] = p1.xy;
1182        let dx = ((x1 - x0) / len.x).abs();
1183        let dy = ((y1 - y0) / len.y).abs();
1184        let mut cost = p0.cost.abs() + p1.cost.abs(); // ≥ 0
1185        if p0.cost * p1.cost < 0. {
1186            // zigzag are bad on a large scale but less important on a
1187            // small scale.
1188            if dx <= 0.01 && dy <= 0.01 { cost *= 0.5 }
1189            else if !(dx <= 0.05 && dy <= 0.05) { cost *= 8. }
1190        }
1191        if dt >= 0.8 { cost }
1192        else {
1193            let dt = dt / 0.8;
1194            dt * dt * (6. + (-8. + 3. * dt) * dt) * cost
1195        }
1196    }
1197
1198}
1199
1200/// Compute the cost of the segment \[`p0`, `p1`\] (taking into
1201/// account `in_vp`) and push it to the queue `pq`.  `p0` is pointed
1202/// to by a list-witness so one can update it from the PQ elements.
1203/// `p0` is updated with the PQ-witness.
1204fn push_segment<D>(
1205    pq: &mut PQ<D>,
1206    p0: &Point<D>, w0: &list::Witness<Point<D>>, p1: Coord,
1207    len: Lengths, in_vp: bool
1208) {
1209    // FIXME: do we really want to push the segment when `!in_vp`?
1210    // In not all points are in the queue, one must beware of the
1211    // validity of witnesses though.
1212    let cost_segment = cost::segment_vp(p0.coord(), p1, len, in_vp);
1213    // The segment is referred to by its first point.
1214    let w = pq.push(cost_segment, w0.clone());
1215    *p0.witness.borrow_mut() = Some(w);
1216}
1217
1218/// With the (new) costs in points `p0` and `p1`, update the position
1219/// of the segment \[`p0`, `p1`\] in the priority queue of `self`.
1220///
1221/// # Safety
1222/// `p0` must be in `pq`, otherwise it is UB.
1223unsafe fn update_segment<D>(
1224    pq: &mut PQ<D>,
1225    p0: &Point<D>,
1226    p1: Coord,
1227    len: Lengths
1228) {
1229    match &*p0.witness.borrow() {
1230        Some(w) => {
1231            let priority = cost::segment(p0.coord(), p1, len);
1232            pq.update_priority(w, priority)
1233        }
1234        None => panic!("Sampling::update_segment: unset witness"),
1235    }
1236}
1237
1238////////////////////////////////////////////////////////////////////////
1239//
1240// Function sampling
1241
1242/// Update the cost of all points in the sampling and add segments
1243/// to the priority queue.
1244fn compute<D>(s: &mut Sampling<D>, in_vp: impl Fn(&Point<D>) -> bool) {
1245    if let Some(len) = s.lengths() {
1246        s.path.first_mut().unwrap().cost = 0.;
1247        s.path.last_mut().unwrap().cost = 0.;
1248        let segments = unsafe { s.path.iter_segments_mut() };
1249        for (p0, w0, pm, p1) in segments {
1250            let p0_is_valid = p0.is_valid();
1251            let p0_in_vp = p0_is_valid && in_vp(p0);
1252            let pm_is_valid = pm.is_valid();
1253            let pm_in_vp;
1254            if let Some(p1) = p1 {
1255                if pm_is_valid {
1256                    pm_in_vp = in_vp(pm);
1257                    if p0_is_valid && p1.is_valid() {
1258                        let c0 = p0.coord();
1259                        let c1 = p1.coord();
1260                        cost::set_middle(c0, pm, c1, len);
1261                    } else {
1262                        pm.cost = cost::HANGING_NODE;
1263                    }
1264                } else { // pm is the location of a cut
1265                    pm_in_vp = false;
1266                    pm.cost = 0.;
1267                }
1268                if p0_is_valid || pm_is_valid {
1269                    // Add segment [p0, pm] to the PQ and set `p0` witness.
1270                    push_segment(&mut s.pq, p0, &w0, pm.coord(), len,
1271                        p0_in_vp || pm_in_vp);
1272                }
1273            } else { // Last segment (no `p1`)
1274                if p0_is_valid || pm_is_valid {
1275                    let mut vp = p0_in_vp;
1276                    if pm.is_valid() { vp = vp || in_vp(pm) };
1277                    push_segment(&mut s.pq, p0, &w0, pm.coord(), len, vp);
1278                }
1279            }
1280        }
1281    }
1282}
1283
1284fn refine_gen<D>(
1285    s: &mut Sampling<D>, n: usize,
1286    mut f: impl FnMut(f64) -> Point<D>,
1287    in_vp: impl Fn(&Point<D>) -> bool
1288) {
1289    let len = match s.lengths() {
1290        Some(lengths) => lengths,
1291        None => return };
1292    macro_rules! r { ($x: ident) => { unsafe { s.path.get(&$x) } } }
1293    macro_rules! m { ($x: ident) => { unsafe { s.path.get_mut(&$x) } } }
1294    macro_rules! prev { ($x: ident) => { unsafe { s.path.prev(& $x) } } }
1295    macro_rules! next { ($x: ident) => { unsafe { s.path.next(& $x) } } }
1296    for _ in 0 .. n {
1297        let mut p0: list::Witness<Point<D>> = match s.pq.pop() {
1298            None => break,
1299            Some(p) => p };
1300        *r!(p0).witness.borrow_mut() = None; // PQ element it points to just popped.
1301        let p1 = next!(p0).unwrap();
1302        // Refine the segment [p0, p1] inserting a middle point `pm`.
1303        let t = (r!(p0).t + r!(p1).t) * 0.5;
1304        let mut pm = f(t);
1305        if r!(p0).is_valid() {
1306            if r!(p1).is_valid() {
1307                let pm = unsafe { s.path.insert_after(&mut p0, pm) };
1308                let mut pm_in_vp = false;
1309                if r!(pm).is_valid() {
1310                    pm_in_vp = in_vp(r!(pm));
1311                    let c0 = r!(p0).coord();
1312                    let c1 = r!(p1).coord();
1313                    cost::set_middle(c0, m!(pm), c1, len);
1314                    let cm = r!(pm).coord();
1315                    if let Some(p_1) = prev!(p0) && r!(p_1).is_valid() {
1316                        let c_1 = r!(p_1).coord();
1317                        cost::set_middle(c_1, m!(p0), cm, len);
1318                        let p_1 = r!(p_1);
1319                        let p0 = r!(p0).coord();
1320                        unsafe { update_segment(&mut s.pq, p_1, p0, len) }
1321                    }
1322                    if let Some(p2) = next!(p1) && r!(p2).is_valid() {
1323                        let c2 = r!(p2).coord();
1324                        cost::set_middle(cm, m!(p1), c2, len);
1325                        let p1 = r!(p1);
1326                        unsafe { update_segment(&mut s.pq, p1, c2, len) }
1327                    }
1328                } else { // `pm` invalid ⟹ cut between `p0` and `p1`
1329                    m!(p0).cost = cost::HANGING_NODE;
1330                    m!(pm).cost = 0.;
1331                    m!(p1).cost = cost::HANGING_NODE;
1332                    if let Some(p_1) = prev!(p0) {
1333                        let p_1 = r!(p_1);
1334                        let p0 = r!(p0).coord();
1335                        unsafe { update_segment(&mut s.pq, p_1, p0, len) }
1336                    }
1337                    if let Some(p2) = next!(p1) {
1338                        let p1 = r!(p1);
1339                        let p2 = r!(p2).coord();
1340                        unsafe { update_segment(&mut s.pq, p1, p2, len) }
1341                    }
1342                }
1343                let vp = pm_in_vp || in_vp(r!(p0));
1344                let cm = r!(pm).coord();
1345                push_segment(&mut s.pq, r!(p0), &p0, cm, len, vp);
1346                let vp = pm_in_vp || in_vp(r!(p1));
1347                let c1 = r!(p1).coord();
1348                push_segment(&mut s.pq, r!(pm), &pm, c1, len, vp);
1349            } else { // `p0` valid, `p1` invalid (i.e. is a cut)
1350                // Thus `p0` is a hanging node.
1351                if pm.is_valid() {
1352                    pm.cost = cost::HANGING_NODE;
1353                    let pm = unsafe { s.path.insert_after(&mut p0, pm) };
1354                    if let Some(p_1) = prev!(p0) && r!(p_1).is_valid() {
1355                        let c_1 = r!(p_1).coord();
1356                        let pm = r!(pm).coord();
1357                        cost::set_middle(c_1, m!(p0), pm, len);
1358                        let p_1 = r!(p_1);
1359                        let p0 = r!(p0).coord();
1360                        unsafe{ update_segment(&mut s.pq, p_1, p0, len) }
1361                    }
1362                    let pm_in_vp = in_vp(r!(pm));
1363                    let vp = pm_in_vp || in_vp(r!(p0));
1364                    let cm = r!(pm).coord();
1365                    let c1 = r!(p1).coord();
1366                    push_segment(&mut s.pq, r!(p0), &p0, cm, len, vp);
1367                    push_segment(&mut s.pq, r!(pm), &pm, c1, len, pm_in_vp)
1368                } else { // `pm` invalid
1369                    // Insert only \[`p0`, `pm`\] and forget
1370                    // \[`pm`, `p1`\].  The cost of `p0` stays
1371                    // `cost::HANGING_NODE`.  We can see this as
1372                    // reducing the uncertainty of the boundary in the
1373                    // segment \[`p0`, `p1`\].
1374                    pm.cost = 0.;
1375                    let pm = {
1376                        if r!(p1).witness.borrow().is_none() {
1377                            // `p1` is not part of a segment.  One can
1378                            // replace it by `pm`.
1379                            unsafe { *s.path.get_mut(&p1) = pm } ;
1380                            p1 // witness for `pm` now.
1381                        } else {
1382                            unsafe { s.path.insert_after(&mut p0, pm) }
1383                        }
1384                    };
1385                    let vp = in_vp(r!(p0));
1386                    let cm = r!(pm).coord();
1387                    push_segment(&mut s.pq, r!(p0), &p0, cm, len, vp)
1388                }
1389            }
1390        } else { // `p0` invalid (i.e., cut) ⟹ `p1` valid
1391            debug_assert!(r!(p1).is_valid());
1392            if pm.is_valid() {
1393                pm.cost = cost::HANGING_NODE;
1394                let pm = unsafe { s.path.insert_after(&mut p0, pm) };
1395                if let Some(p2) = next!(p1) && r!(p2).is_valid() {
1396                    let pm = r!(pm).coord();
1397                    let p2 = r!(p2).coord();
1398                    cost::set_middle(pm, m!(p1), p2, len);
1399                    let p1 = r!(p1);
1400                    unsafe{ update_segment(&mut s.pq, p1, p2, len) }
1401                }
1402                let pm_in_vp = in_vp(r!(pm));
1403                let cm = r!(pm).coord();
1404                let c1 = r!(p1).coord();
1405                push_segment(&mut s.pq, r!(p0), &p0, cm, len, pm_in_vp);
1406                let in_vp = pm_in_vp || in_vp(r!(p1));
1407                push_segment(&mut s.pq, r!(pm), &pm, c1, len, in_vp);
1408            } else { // `pm` invalid ⟹ drop segment \[`p0`, `pm`\].
1409                // Cost of `p1` stays `cost::HANGING_NODE`.
1410                pm.cost = 0.;
1411                let pm = {
1412                    if let Some(p_1) = prev!(p0) {
1413                        if r!(p_1).is_valid() {
1414                            unsafe { s.path.insert_after(&mut p0, pm) }
1415                        } else {
1416                            // `p_1` is the cut ending the previous segment.
1417                            unsafe { *s.path.get_mut(&p0) = pm };
1418                            p0
1419                        }
1420                    } else {
1421                        unsafe { s.path.insert_after(&mut p0, pm) }
1422                    }
1423                };
1424                let vp = in_vp(r!(p1));
1425                let c1 = r!(p1).coord();
1426                push_segment(&mut s.pq, r!(pm), &pm, c1, len, vp)
1427            }
1428        }
1429    }
1430}
1431
1432struct AlmostUniformSampling<D, F>
1433where F: FnMut(f64) -> Point<D> {
1434    f: F,
1435    a: f64,
1436    b: f64,
1437    n: usize,
1438    i: usize,
1439    dt: f64,
1440    random: u32,
1441}
1442
1443/// Return an iterator that yields evaluations of `f` at `n` almost
1444/// equispaced values in the interval \[`a`, `b`\].
1445///
1446/// Remark: The randomization seed only depends on the parameter `n`
1447/// for simple reproducibility.
1448fn almost_uniform_sampling<D, F: FnMut(f64) -> Point<D>>(
1449    f: F,
1450    a: f64, b: f64, n: usize
1451) -> AlmostUniformSampling<D, F> {
1452    let n = n.max(4);
1453    let dt = (b - a) / (n - 3) as f64;
1454    let random = (n as u32).wrapping_mul(1_000_000);
1455    AlmostUniformSampling { f, a, b, n, i: 0, dt, random }
1456}
1457
1458impl<D, F: FnMut(f64) -> Point<D>> Iterator for AlmostUniformSampling<D, F> {
1459    type Item = Point<D>;
1460
1461    fn next(&mut self) -> Option<Point<D>> {
1462        const NORMALIZE_01: f64 = 1. / u32::MAX as f64;
1463
1464        if self.i >= self.n { return None }
1465        let f = &mut self.f;
1466        let v =
1467            if self.i == 0 { f(self.a) }
1468            else if self.i == 1 { f(self.a + 0.0625 * self.dt) }
1469            else if self.i == self.n - 2 { f(self.b - 0.0625 * self.dt) }
1470            else if self.i == self.n - 1 { f(self.b) }
1471            else { // i ∈ {2,...,n-3}
1472                // Pseudorandom number generator from the "Xorshift
1473                // RNGs" paper by George Marsaglia.  See
1474                // https://matklad.github.io/2023/01/04/on-random-numbers.html
1475                // and
1476                // https://github.com/rust-lang/rust/blob/1.55.0/library/core/src/slice/sort.rs#L559-L573
1477                self.random ^= self.random << 13;
1478                self.random ^= self.random >> 17;
1479                self.random ^= self.random << 5;
1480                let rand = self.random as f64 * NORMALIZE_01;
1481                let j = (self.i - 1) as f64 + rand * 0.125 - 0.0625;
1482                f(self.a + j * self.dt)
1483            };
1484        self.i += 1;
1485        Some(v)
1486    }
1487}
1488
1489impl<D> Sampling<D> {
1490    /// Return a sampling from the initial list of `points`.
1491    ///
1492    /// # Preconditions
1493    /// - All `points` are in \[`a`, `b`\].
1494    /// - `points` is sorted in increasing order (if `a` ≤ `b`) or
1495    ///   decreasing otherwise.
1496    fn build(
1497        points: Vec<Point<D>>,
1498        mut f: impl FnMut(f64) -> Point<D>,
1499        a: f64, b: f64, n: usize,
1500        viewport: Option<BoundingBox>
1501    ) -> Self {
1502        if a == b {
1503            let p = f(a);
1504            if p.is_valid() {
1505                return Sampling::singleton(p);
1506            } else {
1507                return Sampling::empty()
1508            }
1509        }
1510        // Uniform sampling requires ≥ 4 points but actually makes no
1511        // sense with less than 10 points.  Thus, if `n < 10`, more
1512        // than `n` evaluations are performed and no refinement is done
1513        // (`n - n0` must not underflow).
1514        let n0 = (n / 10).max(10);
1515        let uniform = almost_uniform_sampling(&mut f, a, b, n0);
1516        let points = MergeSortedIterator::new(
1517            points.into_iter(),
1518            uniform,
1519            if a <= b { Point::cmp_t_incr } else { Point::cmp_t_decr });
1520        let mut s = Sampling::from_point_iterator(points);
1521        match viewport {
1522            Some(vp) => {
1523                let in_vp = |p: &Point<_>| vp.contains(p);
1524                compute(&mut s, in_vp);
1525                refine_gen(&mut s, n.saturating_sub(n0), &mut f, in_vp)
1526            }
1527            None => {
1528                compute(&mut s, |_| true);
1529                refine_gen(&mut s, n.saturating_sub(n0), &mut f, |_| true)
1530            }
1531        }
1532        s
1533    }
1534}
1535
1536
1537new_sampling_fn!(
1538    /// When `f` returns `f64` (resp. `[f64; 2]`), construct a
1539    /// sampling of the *graph* (resp. the *image*) of `f` on the
1540    /// interval \[`a`, `b`\] by evaluating `f` at `n` points (see
1541    /// [`Fun::n`]).  The function `f` may also provide additional
1542    /// data by returning `(y, Data(d))` of type `(f64, Data::<D>)` or
1543    /// `([x, y], Data(d))` of type `([f64; 2], Data::<D>)` (the type
1544    /// parameter `D` will be inferred from the type of the data `d`).
1545    ///
1546    /// If the feature `num-complex` is enabled, complex numbers may
1547    /// also be used.
1548    ,
1549    /// # Examples
1550    ///
1551    /// ```
1552    /// use std::{fs::File, io::BufWriter};
1553    /// use curve_sampling::Sampling;
1554    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1555    /// let s = Sampling::fun(|x| x.sin(), 0., 4.).build();
1556    /// s.write(&mut BufWriter::new(File::create("target/fun1.dat")?))?;
1557    /// let s = Sampling::fun(|x| [x.sin(), x.cos()], 0., 4.).build();
1558    /// s.write(&mut BufWriter::new(File::create("target/fun2.dat")?))?;
1559    /// let s = Sampling::fun(|x| x.sin_cos(), 0., 4.).build();
1560    /// s.write(&mut BufWriter::new(File::create("target/fun3.dat")?))?;
1561    /// # Ok(()) }
1562    /// ```
1563    ///
1564    /// Sampling with associated data:
1565    ///
1566    /// ```
1567    /// use curve_sampling::{Sampling, Data};
1568    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1569    /// fn f(x: f64) -> [f64; 3] {
1570    ///     [x, x*x, x*x*x]
1571    /// }
1572    /// let s = Sampling::fun(|x| {
1573    ///     let fx = f(x);
1574    ///     ([fx[0], fx[1]], Data(fx))
1575    /// }, 0., 1.).build();
1576    /// // ...
1577    /// # Ok(()) }
1578    /// ```
1579    ///
1580    #[cfg_attr(feature = "num-complex", doc = "
1581With the feature `num-complex`, complex-valued functions may be used directly.
1582
1583```
1584use num_complex::Complex64 as C64;
1585use curve_sampling::{Sampling, Data};
1586# fn main() -> Result<(), Box<dyn std::error::Error>> {
1587fn f(t: f64) -> C64 {
1588    C64::i().powf(t)
1589}
1590let s = Sampling::fun(f, 0., 1.).build();
1591// ...
1592# Ok(()) }
1593```
1594")]
1595    fun -> f64,
1596    /// Options for sampling graphs of functions ℝ → ℝ and ℝ → ℝ².
1597    /// See [`Sampling::fun`].
1598    Fun,
1599    FnPoint);
1600
1601impl<F, Y, D> Fun<F, D>
1602where F: FnMut(f64) -> Y,
1603      Y: Img<D> {
1604    /// Return the sampling.
1605    pub fn build(mut self) -> Sampling<D> {
1606        self.eval_init_and_sort();
1607        Sampling::build(
1608            self.init_pt,
1609            |x| (self.f)(x).into_point(x),
1610            self.a, self.b, self.n, self.viewport)
1611    }
1612}
1613
1614
1615new_sampling_fn!(
1616    #[deprecated(note = "Use Sampling::fun instead")]
1617    /// Create a sampling of the *image* of `f` on the interval
1618    /// \[`a`, `b`\] by evaluating `f` at `n` points.
1619    ,
1620    /// # Example
1621    ///
1622    /// ```
1623    /// use std::{fs::File, io::BufWriter};
1624    /// use curve_sampling::Sampling;
1625    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1626    /// let s = Sampling::param(|t| [t.sin(), t.cos()], 0., 4.).build();
1627    /// s.write(&mut BufWriter::new(File::create("target/param.dat")?))?;
1628    /// # Ok(()) }
1629    /// ```
1630    param -> [f64; 2],
1631    #[deprecated(note = "See curve_sampling::Fun instead")]
1632    /// Options for sampling the image of functions ℝ → ℝ².
1633    /// See [`Sampling::param`].
1634    Param,
1635    ParamPoint);
1636
1637#[allow(deprecated)]
1638impl<F, Y, D> Param<F, D>
1639where F: FnMut(f64) -> Y,
1640      Y: Img<D> {
1641    /// Return the sampling.
1642    pub fn build(mut self) -> Sampling<D> {
1643        self.eval_init_and_sort();
1644        Sampling::build(
1645            self.init_pt,
1646            |t| (self.f)(t).into_point(t),
1647            self.a, self.b, self.n, self.viewport)
1648    }
1649}
1650
1651
1652////////////////////////////////////////////////////////////////////////
1653//
1654// Output
1655
1656/// LaTeX output, created by [`Sampling::latex`].
1657///
1658/// # Example
1659///
1660/// ```
1661/// use std::fs::File;
1662/// use curve_sampling::Sampling;
1663/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1664/// let s = Sampling::from_iter([[0., 0.], [1., 1.]]);
1665/// s.latex().write(&mut File::create("target/sampling.tex")?)?;
1666/// # Ok(()) }
1667/// ```
1668pub struct LaTeX<'a, D> {
1669    sampling: &'a Sampling<D>,
1670    n: usize,
1671    color: Option<RGB8>,
1672    arrow: Option<&'a str>,
1673    arrow_pos: Option<f64>,
1674}
1675
1676impl<'a, D> LaTeX<'a, D> {
1677    #[inline]
1678    fn new(s: &'a Sampling<D>) -> Self {
1679        Self { sampling: s,  n: 20_000,  color: None,
1680               arrow: None,  arrow_pos: None }
1681    }
1682
1683    /// Set the maximum number of points of a PGF path to `n`.  If it
1684    /// contains more than `n` points, the sampling curve is drawn as
1685    /// several PGF paths.  Default: 20_000.
1686    pub fn n(&mut self, n: usize) -> &mut Self {
1687        self.n = n;
1688        self
1689    }
1690
1691    /// Set the color of the curve to `color`.  If not specified the
1692    /// active LaTeX color will be used.
1693    pub fn color(&mut self, color: RGB8) -> &mut Self {
1694        self.color = Some(color);
1695        self
1696    }
1697
1698    /// Set the type of arrow to draw to `arrow`.
1699    /// See the documentation of `\pgfsetarrowsend` in the
1700    /// [TikZ manual](https://tikz.dev/base-actions#sec-103.3).
1701    pub fn arrow(&mut self, arrow: &'a str) -> &mut Self {
1702        self.arrow = Some(arrow);
1703        self
1704    }
1705
1706    /// The position of the arrow as a percent of the curve length (in
1707    /// the interval \[0.,1.\]).  If [`LaTeX::arrow`] is specified but
1708    /// not this, it defaults to `0.5`.
1709    pub fn arrow_pos(&mut self, arrow_pos: f64) -> &mut Self {
1710        if ! arrow_pos.is_finite() {
1711            panic!("curve_sampling::LaTeX::arrow_pos: \
1712                    position must be finite");
1713        }
1714        self.arrow_pos = Some(arrow_pos.clamp(0., 1.));
1715        self
1716    }
1717
1718    /// Write the sampling with lines segments.
1719    fn write_with_lines(&self, f: &mut impl Write) -> Result<(), io::Error> {
1720        let mut n = 0;
1721        let mut new_path = true;
1722        for [x,y] in self.sampling.iter() {
1723            if x.is_nan() {
1724                writeln!(f, "\\pgfusepath{{stroke}}")?;
1725                n = 0;
1726                new_path = true;
1727            } else {
1728                n += 1;
1729                if new_path {
1730                    writeln!(f, "\\pgfpathmoveto{{\\pgfpointxy\
1731                                 {{{x}}}{{{y}}}}}")?
1732                } else if n >= self.n {
1733                    write!(f, "\\pgfpathlineto{{\\pgfpointxy\
1734                               {{{x}}}{{{y}}}}}\n\
1735                               \\pgfusepath{{stroke}}\n\
1736                               \\pgfpathmoveto{{\\pgfpointxy\
1737                               {{{x}}}{{{y}}}}}\n")?;
1738                    n = 0;
1739                } else {
1740                    writeln!(f, "\\pgfpathlineto{{\\pgfpointxy\
1741                                 {{{x}}}{{{y}}}}}")?
1742                }
1743                new_path = false;
1744            }
1745        }
1746        Ok(())
1747    }
1748
1749    /// Write the path, each continuous sub-path with an arrow.
1750    fn write_with_arrows(&self, f: &mut impl Write, arrow: &str,
1751                         arrow_pos: f64) -> Result<(), io::Error> {
1752        // Compute the length of all sub-paths.
1753        let mut lens = vec![];
1754        let mut prev_pt: Option<[f64; 2]> = None;
1755        let mut cur_len = 0.;
1756        for p @ [x, y] in self.sampling.iter() {
1757            if x.is_nan() {
1758                lens.push(arrow_pos * cur_len);
1759                prev_pt = None;
1760                cur_len = 0.;
1761            } else {
1762                if let Some([x0, y0]) = prev_pt {
1763                    cur_len += (x - x0).hypot(y - y0);
1764                }
1765                prev_pt = Some(p);
1766            }
1767        }
1768        lens.push(arrow_pos * cur_len);
1769        if lens.is_empty() { return Ok(()) }
1770        let mut lens = lens.iter();
1771        let mut rem_len = *lens.next().unwrap(); // remaining before arrow
1772        prev_pt = None;
1773        let mut n = 0;
1774        for p @ [x, y] in self.sampling.iter() {
1775            if x.is_nan() {
1776                writeln!(f, "\\pgfusepath{{stroke}}")?;
1777                rem_len = *lens.next().unwrap();
1778                prev_pt = None;
1779            } else {
1780                n += 1;
1781                if let Some([x0, y0]) = prev_pt {
1782                    let dx = x - x0;
1783                    let dy = y - y0;
1784                    let l = dx.hypot(dy);
1785                    if rem_len <= l {
1786                        writeln!(f, "\\pgfusepath{{stroke}}")?;
1787                        // Drawing a long path with an arrow specified is
1788                        // extremely expensive.  Just draw the current segment.
1789                        let pct = rem_len / l;
1790                        if pct <= 1e-14 {
1791                            write!(f, "\\pgfsetarrowsstart{{{arrow}}}\n\
1792                                       \\pgfpathmoveto{{\\pgfpointxy
1793                                       {{{x0}}}{{{y0}}}}}\n\
1794                                       \\pgfpathlineto{{\\pgfpointxy\
1795                                       {{{x}}}{{{y}}}}}\n\
1796                                       \\pgfusepath{{stroke}}\n\
1797                                       \\pgfsetarrowsend{{}}\n\
1798                                       \\pgfpathmoveto{{\\pgfpointxy\
1799                                       {{{x}}}{{{y}}}}}\n")?;
1800                        } else {
1801                            let xm = x0 + pct * dx;
1802                            let ym = y0 + pct * dy;
1803                            write!(f, "\\pgfsetarrowsend{{{arrow}}}\n\
1804                                       \\pgfpathmoveto{{\\pgfpointxy\
1805                                       {{{x0}}}{{{y0}}}}}\n\
1806                                       \\pgfpathlineto{{\\pgfpointxy\
1807                                       {{{xm}}}{{{ym}}}}}\n\
1808                                       \\pgfusepath{{stroke}}\n\
1809                                       \\pgfsetarrowsend{{}}\n\
1810                                       \\pgfpathmoveto{{\\pgfpointxy\
1811                                       {{{xm}}}{{{ym}}}}}\n\
1812                                       \\pgfpathlineto{{\\pgfpointxy\
1813                                       {{{x}}}{{{y}}}}}\n")?;
1814                            n = 2;
1815                        }
1816                        rem_len = f64::INFINITY; // No more arrow for this sub-path
1817                    } else if n >= self.n {
1818                        write!(f, "\\pgfpathlineto{{\\pgfpointxy\
1819                                   {{{x}}}{{{y}}}}}\n\
1820                                   \\pgfusepath{{stroke}}\n\
1821                                   \\pgfpathmoveto{{\\pgfpointxy\
1822                                   {{{x}}}{{{y}}}}}\n")?;
1823                        n = 0;
1824                    } else {
1825                        writeln!(f, "\\pgfpathlineto{{\\pgfpointxy\
1826                                     {{{x}}}{{{y}}}}}")?
1827                    }
1828                    rem_len -= l;
1829                } else {
1830                    // No previous point.  New sub-path.
1831                    writeln!(f, "\\pgfpathmoveto{{\\pgfpointxy\
1832                                 {{{x}}}{{{y}}}}}")?
1833                }
1834                prev_pt = Some(p);
1835            }
1836        }
1837        Ok(())
1838    }
1839
1840    /// Write the sampling to the formatter as PGF/TikZ commands.
1841    pub fn write(&self, f: &mut impl Write) -> Result<(), io::Error> {
1842        writeln!(f, "% Written by the Rust curve-sampling crate.")?;
1843        writeln!(f, "\\begin{{pgfscope}}")?;
1844        if let Some(RGB8 {r, g, b}) = self.color {
1845            write!(f, "\\definecolor{{RustCurveSamplingColor}}{{RGB}}\
1846                       {{{r},{g},{b}}}\n\
1847                       \\pgfsetstrokecolor{{RustCurveSamplingColor}}\n")?
1848        }
1849        match (self.arrow, self.arrow_pos) {
1850            (None, None) => self.write_with_lines(f)?,
1851            (Some(arrow), None) =>
1852                self.write_with_arrows(f, arrow, 0.5)?,
1853            (None, Some(arrow_pos)) =>
1854                self.write_with_arrows(f, ">",arrow_pos)?,
1855            (Some(arrow), Some(arrow_pos)) =>
1856                self.write_with_arrows(f, arrow, arrow_pos)?,
1857        }
1858        write!(f, "\\pgfusepath{{stroke}}\n\\end{{pgfscope}}\n")
1859    }
1860}
1861
1862/// # Output
1863impl<D> Sampling<D> {
1864    /// Write the sampling `self` using PGF/TikZ commands.
1865    pub fn latex(&self) -> LaTeX<'_, D> { LaTeX::new(self) }
1866
1867    /// Write the sampling to `f` in a tabular form: each point is
1868    /// written as "x y" on a single line (in scientific notation).
1869    /// If the path is interrupted, a blank line is printed.  This
1870    /// format is compatible with Gnuplot.
1871    pub fn write(&self, f: &mut impl Write) -> Result<(), io::Error> {
1872        for [x, y] in self.iter() {
1873            if x.is_nan() {
1874                writeln!(f)?
1875            } else {
1876                writeln!(f, "{x:e} {y:e}")?
1877            }
1878        }
1879        Ok(())
1880    }
1881}
1882
1883impl<D> Display for Sampling<D> {
1884    /// Display the sampling in a tabular form: each point is written
1885    /// as "x y" on a single line (in scientific notation).  If the
1886    /// path is interrupted, a blank line is printed.  This format is
1887    /// compatible with Gnuplot.
1888    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
1889        for [x, y] in self.iter() {
1890            if x.is_nan() {
1891                writeln!(f)?
1892            } else {
1893                writeln!(f, "{x:e} {y:e}")?
1894            }
1895        }
1896        Ok(())
1897    }
1898}
1899
1900////////////////////////////////////////////////////////////////////////
1901//
1902// Tests
1903
1904#[cfg(test)]
1905mod tests {
1906    use std::{error::Error,
1907              fs::File,
1908              io::Write, path::Path};
1909    use crate::{BoundingBox, Data, MergeSortedIterator, NoData, Point, Sampling};
1910
1911    #[test]
1912    fn merge_sorted_iterator() {
1913        let i1 = [1, 3, 5, 7].into_iter();
1914        let i2 = [1, 2, 5, 8].into_iter();
1915        let i = MergeSortedIterator::new(i1, i2, |i1, i2| i1.cmp(i2));
1916        assert_eq!(i.collect::<Vec<_>>(), [1, 2, 3, 5, 7, 8])
1917    }
1918
1919    type R<T> = Result<T, Box<dyn Error>>;
1920
1921    fn xy_of_sampling<D>(s: &Sampling<D>) -> Vec<Option<(f64, f64)>> {
1922        s.path.iter().map(|p| {
1923            if p.is_valid() { Some((p.xy[0], p.xy[1])) } else { None }
1924        })
1925            .collect()
1926    }
1927
1928    #[test]
1929    fn clone_sampling() {
1930        let s = Sampling::from_iter([[0.,0.], [1.,1.]]);
1931        let xy0: Vec<_> = s.iter().collect();
1932        let xy1: Vec<_> = s.clone().iter().collect();
1933        assert_eq!(xy0, xy1)
1934    }
1935
1936    #[test]
1937    fn io() {
1938        let s = Sampling::from_iter([[0.,0.], [1.,1.]]);
1939        assert_eq!(xy_of_sampling(&s), vec![Some((0.,0.)), Some((1.,1.))]);
1940        let s = Sampling::from_iter([[0.,0.], [1.,1.], [f64::NAN, 1.],
1941                                     [2.,2.]]);
1942        assert_eq!(xy_of_sampling(&s),
1943                   vec![Some((0.,0.)), Some((1.,1.)), None, Some((2.,2.))]);
1944    }
1945
1946    #[test]
1947    fn bounding_box_singleton() {
1948        let s = Sampling::singleton(
1949            Point::new_unchecked(0., [1., 2.], NoData::new()));
1950        let bb = BoundingBox {xmin: 1., xmax: 1., ymin: 2., ymax: 2.};
1951        assert_eq!(s.bounding_box(), bb);
1952    }
1953
1954        #[test]
1955    fn singleton_has_empty_bounding_box() {
1956        let s = Sampling::singleton(
1957            Point::new_unchecked(0., [1., 2.], NoData::new()));
1958        assert!(s.bounding_box().is_empty());
1959    }
1960
1961    fn test_clip(bb: BoundingBox,
1962                 path: Vec<[f64;2]>,
1963                 expected: Vec<Option<(f64,f64)>>) {
1964        let s = Sampling::from_iter(path).clip(bb);
1965        assert_eq!(xy_of_sampling(&s), expected);
1966    }
1967
1968    #[test]
1969    fn clip_base () {
1970        let bb = BoundingBox { xmin: 0.,  xmax: 3., ymin: 0.,  ymax: 2.};
1971        test_clip(bb, vec![[0.,1.], [2.,3.]],
1972                  vec![Some((0.,1.)), Some((1.,2.))]);
1973        test_clip(bb,
1974                  vec![[-1.,0.], [2.,3.], [4.,1.]],
1975                  vec![Some((0.,1.)), Some((1.,2.)), None, Some((3., 2.))]);
1976        test_clip(bb,
1977                  vec![[0.,1.], [2.,3.], [4.,1.], [2.,1.], [2., -1.],
1978                       [0., -1.], [0., 1.] ],
1979                  vec![Some((0.,1.)), Some((1.,2.)), None,
1980                       Some((3., 2.)), None,
1981                       Some((3., 1.)), Some((2., 1.)), Some((2., 0.)), None,
1982                       Some((0., 0.)), Some((0., 1.))]);
1983    }
1984
1985    #[test]
1986    fn clip_empty() {
1987        let bb = BoundingBox {xmin: 0., xmax: 1., ymin: 0., ymax: 1.};
1988        let path = xy_of_sampling(&Sampling::<()>::empty().clip(bb));
1989        assert_eq!(path, vec![]);
1990    }
1991
1992    #[test]
1993    fn clip_double_cut() {
1994        let bb = BoundingBox { xmin: 0.,  xmax: 4., ymin: 0.,  ymax: 2.};
1995        test_clip(bb,
1996                  vec![[1., 2.], [2.,3.], [5.,0.], [-1., 0.] ],
1997                  vec![Some((1., 2.)), None,
1998                       Some((3., 2.)), Some((4., 1.)), None,
1999                       Some((4., 0.)), Some((0., 0.)) ]);
2000    }
2001
2002    #[test]
2003    fn clip_almost_horiz() {
2004        let bb = BoundingBox { xmin: 0.,  xmax: 2., ymin: -1.,  ymax: 1.};
2005        test_clip(bb,
2006                  vec![[1., 1e-100], [3., -1e-100] ],
2007                  vec![Some((1., 1e-100)), Some((2., 0.))]);
2008    }
2009
2010    #[test]
2011    fn clip_touches_1pt_cut() {
2012        let bb = BoundingBox {xmin: 0., xmax: 2., ymax: 0., ymin: -1.};
2013        let cut = [f64::NAN, f64::NAN];
2014        test_clip(bb,
2015                  vec![[0.,1.], cut, [1., 0.], cut, cut, [2., 1.]],
2016                  vec![Some((1., 0.))])
2017    }
2018
2019    #[test]
2020    fn clip_final_cut() {
2021        let bb = BoundingBox {xmin: 0., xmax: 1., ymin: 0., ymax: 2.};
2022        test_clip(bb,
2023                  vec![[0., 0.], [2., 2.]],
2024                  vec![Some((0., 0.)), Some((1., 1.))])
2025    }
2026
2027    #[test]
2028    fn small_n() {
2029        // The initial exploration uses at least 10 points, even if
2030        // fewer evaluations were requested.  The number of refinement
2031        // steps `n - n0` used to underflow in that case: a panic in
2032        // debug mode, an endless loop in release mode.
2033        for n in 2 ..= 12 {
2034            let mut evals = 0;
2035            let s = Sampling::fun(|x: f64| { evals += 1; x.sin() }, 0., 3.)
2036                .n(n).build();
2037            assert_eq!(evals, n.max(10), "n = {n}");
2038            assert_eq!(s.len(), evals);
2039        }
2040    }
2041
2042    #[test]
2043    fn uniform1() {
2044        let s = Sampling::uniform(|x| x, 0., 4.).n(3)
2045            .init(&[1.]).init_pt([(3., 0.)]).build();
2046        assert_eq!(xy_of_sampling(&s),
2047                   vec![Some((0.,0.)), Some((1.,1.)), Some((2.,2.)),
2048                        Some((3., 0.)), Some((4.,4.))]);
2049    }
2050
2051    #[test]
2052    fn uniform2() {
2053        let s = Sampling::uniform(|x| (4. - x).sqrt(), 0., 6.).n(4).build();
2054        assert_eq!(xy_of_sampling(&s),
2055                   vec![Some((0.,2.)), Some((2., 2f64.sqrt())),
2056                        Some((4., 0.)), None]);
2057    }
2058
2059    #[test]
2060    fn iter_data() {
2061        let s = Sampling::uniform(|x| (x, Data(x as i32)), 0., 4.).n(3)
2062            .init(&[1.]).init_pt([(3., (0., Data(-1)))]).build();
2063        let expected = vec![
2064            ([0.,0.], &0), ([1.,1.], &1), ([2.,2.], &2), ([3., 0.], &-1),
2065            ([4.,4.], &4)];
2066        let (_, size_hint) = s.iter_data().size_hint();
2067        assert_eq!(size_hint, Some(expected.len()));
2068        for (i, d) in s.iter_data().enumerate() {
2069            assert_eq!(d, expected[i]);
2070        }
2071    }
2072
2073    #[test]
2074    fn into_iter_data() {
2075        let s = Sampling::uniform(|x| (x, Data(x as i32)), 0., 4.).n(3)
2076            .init(&[1.]).init_pt([(3., (0., Data(-1)))]).build();
2077        let expected = vec![
2078            ([0.,0.], 0), ([1.,1.], 1), ([2.,2.], 2), ([3., 0.], -1),
2079            ([4.,4.], 4)];
2080        let (_, size_hint) = s.iter_data().size_hint();
2081        assert_eq!(size_hint, Some(expected.len()));
2082        for (i, d) in s.into_iter_data().enumerate() {
2083            assert_eq!(d, expected[i]);
2084        }
2085    }
2086
2087    /// In order the judge the quality of the sampling, we save it
2088    /// with the internal cost data.
2089    fn write_with_point_costs<D>(
2090        s: &Sampling<D>,
2091        fname: impl AsRef<Path>
2092    ) -> R<()> {
2093        let mut fh = File::create(fname)?;
2094        for p in s.path.iter() {
2095            if p.is_valid() {
2096                let [x, y] = p.xy;
2097                writeln!(fh, "{x} {y} {}", p.cost)?;
2098            } else {
2099                writeln!(fh)?;
2100            }
2101        }
2102        Ok(())
2103    }
2104
2105    fn write_segments<D>(mut s: Sampling<D>, fname: impl AsRef<Path>) -> R<()> {
2106        let mut fh = File::create(fname)?;
2107        let mut seg = vec![];
2108        loop {
2109            let priority = s.pq.max_priority();
2110            if let Some(p0) = s.pq.pop() {
2111                let p1 = unsafe { s.path.next(&p0).unwrap() };
2112                let p1 = unsafe { s.path.get(&p1) };
2113                let p0 = unsafe { s.path.get(&p0) };
2114                let tm = (p0.t + p1.t) / 2.;
2115                seg.push((tm, p0.coord(), p1.coord(), priority))
2116            } else {
2117                break;
2118            }
2119        }
2120        seg.sort_by(|(t1,_,_,_), (t2,_,_,_)| t1.partial_cmp(t2).unwrap());
2121        for (tm, p0, p1, priority) in seg {
2122            writeln!(fh, "{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}",
2123                tm, p0.t, p0.xy[0], p0.xy[1],
2124                p1.t, p1.xy[0], p1.xy[1], priority)?;
2125        }
2126        Ok(())
2127    }
2128
2129    #[derive(Clone)]
2130    struct Plot {
2131        xmin: f64,  xmax: f64,
2132        ymin: f64,  ymax: f64,
2133        n: usize,
2134        init: Vec<f64>,
2135    }
2136
2137    impl Plot {
2138        /// Return the Gnuplot instructions to plot the data.
2139        fn plot<F>(&self, f: F, title: &str) -> R<String>
2140        where F: FnMut(f64) -> f64 {
2141            let vp = BoundingBox { xmin: self.xmin, xmax: self.xmax,
2142                                   ymin: self.ymin, ymax: self.ymax };
2143            let s = Sampling::fun(f, self.xmin, self.xmax)
2144                .n(self.n).init(self.init.iter()).viewport(vp).build();
2145            static mut NDAT: usize = 0;
2146            let ndat = unsafe { NDAT += 1;  NDAT };
2147            println!("Horror {ndat}");
2148            s.pq.print_stats();
2149            let dir = Path::new("target");
2150            let fname = format!("horror{}.dat", ndat);
2151            s.write(&mut File::create(dir.join(&fname))?)?;
2152            let fname_p = format!("horror{}_p.dat", ndat);
2153            write_with_point_costs(&s, dir.join(&fname_p))?;
2154            let fname_s = format!("horror{}_s.dat", ndat);
2155            write_segments(s, dir.join(&fname_s))?;
2156        Ok(format!(
2157            "unset title\n\
2158             unset y2tics\n\
2159             plot [{}:{}] \"{}\" with l lt 5 title \"{}\", \
2160             \"{}\" with p lt 3 pt 6 ps 0.2 title \"n={}\"\n\
2161             set title \"#{ndat} Restricted to viewport [{}:{}]×[{}:{}]\"\n\
2162             set y2tics\n\
2163             set y2range [-1e-6:]\n\
2164             plot [{}:{}] [{}:{}] \"{}\" with l lt 5 title \"{}\", \
2165             \"{}\" with p lt 3 pt 7 ps 0.2 title \"n={}\", \
2166             \"{}\" using 1:3  with lp ps 0.2 lt rgb \"#760b0b\" \
2167             title \"cost points\", \
2168             \"{}\" using 1:8 with lp ps 0.2 lt rgb \"#909090\" \
2169             axes x1y2 title \"cost segments\"\n",
2170            self.xmin, self.xmax, &fname, title, &fname, self.n,
2171            self.xmin, self.xmax, self.ymin, self.ymax,
2172            self.xmin, self.xmax, self.ymin, self.ymax, &fname, title,
2173            &fname, self.n, &fname_p, &fname_s))
2174        }
2175    }
2176
2177    #[test]
2178    #[cfg_attr(miri, ignore)] // very slow under Miri
2179    fn horror() -> R<()> {
2180        let d = Plot {
2181            xmin: -5., xmax: 5., ymin: -5., ymax: 5., n: 100, init: vec![] };
2182        macro_rules! p {
2183            ($($id:ident $l:tt $e:expr),*) => {
2184                Plot{ $($id: $e,)* ..d.clone() } };
2185        }
2186        let s = [
2187            p!(n: 10).plot(|_| 2., "x ↦ 2")?,
2188            p!().plot(|x| x, "x ↦ x")?,
2189            p!().plot(|x| 5. * x, "x ↦ 5x")?,
2190            p!().plot(|x| 1e6 * x, "x ↦ 10⁶ x")?, // high slope
2191            p!().plot(|x| 1e50 * x, "x ↦ 10⁵⁰ x")?, // high slope
2192            p!().plot(|x| 1. / x, "x ↦ 1/x")?, // check singularity
2193            p!(xmin: 0., xmax: 5., ymax: 100.).plot(
2194                |x| 1. / x, "x ↦ 1/x")?, // singularity at starting point
2195            p!(xmin: -0.4, xmax: 2., ymin: 0., ymax: 1.6, n: 50).plot(
2196                |x| x.sqrt(), "x ↦ √x")?,
2197            // Test cuts also to the right:
2198            p!(xmin: -2., xmax: 1., ymin: 0., ymax: 1.6, n: 50).plot(
2199                |x| (-x).sqrt(), "x ↦ √(-x)")?,
2200            p!(n: 200).plot(|x| x.tan(), "tan")?,
2201            p!().plot(|x| 1. / x.abs(), "x ↦ 1/|x|")?,
2202            p!(xmin: -6., xmax: 6., ymin: -2., ymax: 2.).plot(
2203                |x| (1. + x.cos().sin()).ln(), "1 + sin(cos x)")?,
2204            p!(xmin: 0., xmax: 6.28, ymin: -1.5, ymax: 1.5, n: 400).plot(
2205                |x| x.powi(3).sin() + x.powi(3).cos(), "sin x³ + cos x³")?,
2206            p!(xmin: -5., xmax:200., ymin: -1., ymax: 1., n: 400).plot(
2207                |x| x.sin(), "sin")?,
2208            // Examples from R. Avitzur, O. Bachmann, N. Kajler, "From
2209            // Honest to Intelligent Plotting", proceedings of ISSAC'
2210            // 95, pages 32-41, July 1995.
2211            p!(xmin: -4., xmax: 4., ymin: -1., ymax: 1.).plot(
2212                |x| (300. * x).sin(), "sin(300 x)")?,
2213            p!(xmin: -4., xmax: 4., ymin: -1., ymax: 1., n: 1000).plot(
2214                |x| (300. * x).sin(), "sin(300 x)")?,
2215            p!(xmin: -2., xmax: 2., ymin: 0., ymax: 3.).plot(
2216                |x| 1. + x * x + 0.0125 * (1. - 3. * (x - 1.)).abs().ln(),
2217                "1 + x² + 0.0125 ln|1 - 3(x-1)|")?,
2218            p!(xmin: -2., xmax: 2., ymin: 0., ymax: 3., n: 300,
2219                init:vec![4. / 3.]).plot(
2220                    |x| 1. + x * x + 0.0125 * (1. - 3. * (x - 1.)).abs().ln(),
2221                   "1 + x² + 0.0125 ln|1 - 3(x-1)| (specifying x:4/3)")?,
2222            p!(xmin: -0.5, xmax: 0.5, ymin: -1., ymax: 1.).plot(
2223                |x| x * (1. / x).sin(), "x sin(1/x)")?,
2224            p!(xmin: -0.5, xmax: 0.5, ymin: -1., ymax: 1., n:200).plot(
2225                |x| x * (1. / x).sin(), "x sin(1/x)")?,
2226            p!(xmin: -2., xmax: 2., ymin: -1., ymax: 1.).plot(
2227                |x| (1. / x).sin(), "sin(1/x)")?,
2228            p!(xmin: -2., xmax: 2., ymin: -1., ymax: 1., n: 400).plot(
2229                |x| (1. / x).sin(), "sin(1/x)")?,
2230            p!(xmin: -4., xmax: 4., ymin: -1., ymax: 1.).plot(
2231                |x| x.powi(4).sin(), "sin(x⁴)")?,
2232            p!(xmin: -4., xmax: 4., ymin: -1., ymax: 1., n: 600).plot(
2233                |x| x.powi(4).sin(), "sin(x⁴)")?,
2234            p!(xmin: -6., xmax: 6., ymin: -1., ymax: 1.).plot(
2235                |x| x.exp().sin(), "sin(exp x)")?,
2236            p!(xmin: -6., xmax: 6., ymin: -1., ymax: 1., n: 500).plot(
2237                |x| x.exp().sin(), "sin(exp x)")?,
2238            p!(xmin: -10., xmax: 10., ymin: 0., ymax: 10.).plot(
2239                |x| 1. / x.sin(), "1 / sin x")?,
2240            p!(xmin: -6., xmax: 6., ymin: -1., ymax: 1.).plot(
2241                |x| x.sin() / x, "(sin x)/x")?,
2242            p!(xmin: -2., xmax: 2., ymin: -15., ymax: 15.).plot(
2243                |x| (x.powi(3) - x + 1.).tan() + 1. / (x + 3. * x.exp()),
2244                "tan(x³ - x + 1) + 1/(x + 3 eˣ)")?,
2245            p!(xmin: 0., xmax: 17., ymin: 0., ymax: 2.).plot(
2246                |x| (1. + x.cos()) * (-0.1 * x).exp(),
2247                "(1 + cos x) exp(-x/10)")?,
2248            p!(xmin: -2., xmax: 17., ymin: 0., ymax: 2.).plot(
2249                |x| (1. + x.cos()) * (-0.1 * x).exp(),
2250                "(1 + cos x) exp(-x/10)")?,
2251            p!(xmin: 0., xmax: 17., ymin: 0., ymax: 2.).plot(
2252                |x| (1. + x.cos()) * (-0.01 * x * x).exp(),
2253                "(1 + cos x) exp(-x²/100)")?,
2254        ].join("");
2255        let mut fh = File::create("target/horror.gp")?;
2256        write!(fh, "set terminal pdfcairo\n\
2257                    set output \"horror.pdf\"\n\
2258                    set grid\n\
2259                    {s}\n")?;
2260        Ok(())
2261    }
2262}