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