1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3 
4   Copyright (C) 2021 Daniel Eble <nine.fierce.ballads@gmail.com>
5 
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10 
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15 
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include "interval.hh"
21 
22 #include "interval.tcc"
23 
24 #include "yaffut.hh"
25 
26 #include <chrono>
27 #include <cmath>
28 #include <limits>
29 #include <string>
30 
31 namespace
32 {
33 
34 // This helps see which operations Interval_t<T> requires of T.
35 //
36 // This is a sandbox, not a design specification.  If something is omitted or
37 // deleted here, it doesn't mean that it is forever forbidden from use.
38 class Mint // mock int
39 {
40 private:
41   using M = Mint; // shorthand
42   int v_ {};
43 
44 public:
45   Mint () = default;
46   Mint (const Mint &) = default;
47   Mint (Mint &&) = default;
48   ~Mint () = default;
49 
50   // conversion
Mint(int v)51   constexpr explicit Mint (int v) : v_ (v) {}
52 
53   // special values
infinity()54   static constexpr M infinity () { return M (100); }
55 
56   // assignment
57   M &operator = (const M &) = default;
58   M &operator = (M &&) = default;
59 
60   // negation
operator -(M a)61   friend constexpr M operator - (M a) { return M (-a.v_); }
62 
63   // addition
operator +=(M b)64   M &operator += (M b) { v_ += b.v_; return *this; }
operator -=(M b)65   M &operator -= (M b) { v_ -= b.v_; return *this; }
operator +(M a,M b)66   friend constexpr M operator + (M a, M b) { return M (a.v_ + b.v_); }
operator -(M a,M b)67   friend constexpr M operator - (M a, M b) { return M (a.v_ - b.v_); }
68 
69   // scaling
70   M &operator *= (int) = delete;
71   M &operator /= (int) = delete;
72   friend constexpr M operator * (M, int) = delete;
operator /(M a,int b)73   friend constexpr M operator / (M a, int b) { return M (a.v_ / b); }
74 
75   // comparison
operator !=(M a,M b)76   friend constexpr bool operator != (M a, M b) { return a.v_ != b.v_; }
operator <(M a,M b)77   friend constexpr bool operator < (M a, M b) { return a.v_ < b.v_; }
operator <=(M a,M b)78   friend constexpr bool operator <= (M a, M b) { return a.v_ <= b.v_; }
operator ==(M a,M b)79   friend constexpr bool operator == (M a, M b) { return a.v_ == b.v_; }
operator >(M a,M b)80   friend constexpr bool operator > (M a, M b) { return a.v_ > b.v_; }
operator >=(M a,M b)81   friend constexpr bool operator >= (M a, M b) { return a.v_ >= b.v_; }
82 
to_string(M a)83   friend std::string to_string (M a) { return std::to_string (a.v_); }
84 };
85 
86 }
87 
88 static inline std::ostream &
operator <<(std::ostream & os,Mint m)89 operator << (std::ostream &os, Mint m) // for Yaffut
90 {
91   return os << to_string (m);
92 }
93 
94 template INTERVAL__INSTANTIATE (Mint);
95 
96 using IVM = Interval_t<Mint>;
97 
98 class Interval_test
99 {
test_init_default()100   static void test_init_default ()
101   {
102     constexpr IVM iv;
103 
104     static_assert (iv.left () == Mint (100), "");
105     static_assert (iv.right () == Mint (-100), "");
106 
107     static_assert (iv.length () == Mint (0), "");
108   }
109 
test_init_point()110   static void test_init_point ()
111   {
112     constexpr IVM iv (Mint (23));
113 
114     static_assert (iv.left () == Mint (23), "");
115     static_assert (iv.right () == Mint (23), "");
116 
117     static_assert (iv.length () == Mint (0), "");
118   }
119 
test_init_list()120   static void test_init_list ()
121   {
122     constexpr IVM iv {Mint (10), Mint (20)};
123 
124     static_assert (iv.left () == Mint (10), "");
125     static_assert (iv.right () == Mint (20), "");
126 
127     static_assert (iv.length () == Mint (10), "");
128   }
129 
test_init_list_assign()130   static void test_init_list_assign ()
131   {
132     constexpr IVM iv = {Mint (40), Mint (30)};
133 
134     static_assert (iv.left () == Mint (40), "");
135     static_assert (iv.right () == Mint (30), "");
136 
137     static_assert (iv.length () == Mint (0), "");
138   }
139 
test_init_implicit_conversion()140   static Interval_t<int> test_init_implicit_conversion ()
141   {
142     constexpr Interval_t<signed char> input_iv {-2, 3};
143 
144     // Interval_t<signed char> can be converted (here explicitly, below
145     // implicitly) to Interval_t<int> because signed char is implicitly
146     // convertible to int.
147     //
148     // TODO: It would be good to verify a negative case too, e.g. that
149     // Interval_t<signed char> is not convertible to IVM because
150     // signed char is not implicitly convertible to Mint (because the
151     // implementation of Mint prevents it).
152     constexpr Interval_t<int> test_iv (input_iv);
153     static_assert (test_iv.left () == -2, "");
154     static_assert (test_iv.right () == 3, "");
155 
156     return input_iv; // implicitly converted
157   }
158 
test_longest()159   static void test_longest ()
160   {
161     constexpr auto iv = IVM::longest ();
162     static_assert (iv.left () == Mint (-100), "");
163     static_assert (iv.right () == Mint (100), "");
164   }
165 
test_std_chrono_time_point()166   static void test_std_chrono_time_point ()
167   {
168     using Clock = std::chrono::steady_clock;
169     constexpr Clock::duration d (0);
170     constexpr Clock::time_point tp (d);
171 
172     Interval_t<Clock::time_point> iv {tp, tp};
173 
174     // Checking arithmetic is a job for other test cases.  The goal here is to
175     // show that that the types (dimensions) are correct.
176     static_cast<void> (iv = (iv += d));
177     static_cast<void> (iv = (iv -= d));
178     static_cast<void> (iv = intersection (iv, iv));
179     static_cast<void> (iv = iv + d);
180     static_cast<void> (iv = iv - d);
181     static_cast<void> (iv = iv.union_disjoint (iv, d, Direction::positive ()));
182     static_cast<void> (iv.center () == tp);
183     static_cast<void> (iv.contains (tp));
184     static_cast<void> (iv.distance (tp) == d);
185     static_cast<void> (iv.intersect (iv));
186     static_cast<void> (iv.is_empty ());
187     static_cast<void> (iv.left_less (iv, iv));
188     static_cast<void> (iv.length () == d);
189     static_cast<void> (iv.swap ());
190     static_cast<void> (iv.translate (d));
191     static_cast<void> (iv.unite (iv));
192     static_cast<void> (iv.unite_disjoint (iv, d, Direction::negative ()));
193     static_cast<void> (iv.widen (d));
194   }
195 };
196 
TEST(Interval_test,center)197 TEST (Interval_test, center)
198 {
199   // TODO: center() asserts that the interval is not empty.  Maybe it should
200   // instead return a specified fallback value so we could test it.
201 
202   {
203     const IVM iv (Mint (13));
204     EQUAL (iv.center (), Mint (13));
205   }
206 
207   {
208     const IVM iv (Mint (10), Mint (20));
209     EQUAL (iv.center (), Mint (15));
210   }
211 }
212 
213 // contains
214 
215 static_assert (IVM ().contains (Mint (0)) == false, "");
216 
217 static_assert (IVM (Mint (4)).contains (Mint (3)) == false, "");
218 static_assert (IVM (Mint (4)).contains (Mint (4)) == true, "");
219 static_assert (IVM (Mint (4)).contains (Mint (5)) == false, "");
220 
221 static_assert (IVM (Mint (-22), Mint (7)).contains (Mint (-23)) == false, "");
222 static_assert (IVM (Mint (-22), Mint (7)).contains (Mint (-22)) == true, "");
223 static_assert (IVM (Mint (-22), Mint (7)).contains (Mint (0)) == true, "");
224 static_assert (IVM (Mint (-22), Mint (7)).contains (Mint (7)) == true, "");
225 static_assert (IVM (Mint (-22), Mint (7)).contains (Mint (8)) == false, "");
226 
227 template <typename T>
228 class Interval_math_test
229 {
230 protected:
231   using IVT = Interval_t<T>;
232 
neg_infinity()233   static constexpr T neg_infinity () { return Interval_traits<T>::min (); }
pos_infinity()234   static constexpr T pos_infinity () { return Interval_traits<T>::max (); }
235 
236 protected:
test_add_point()237   void test_add_point ()
238   {
239     // empty; add -inf
240     {
241       IVT iv; // empty
242       iv.add_point (neg_infinity ());
243       EQUAL (iv.left (), neg_infinity ());
244       EQUAL (iv.right (), neg_infinity ());
245     }
246 
247     // empty; add 0
248     {
249       IVT iv; // empty
250       iv.add_point (T (0));
251       EQUAL (iv.left (), T (0));
252       EQUAL (iv.right (), T (0));
253       EQUAL (iv.length (), T (0));
254     }
255 
256     // empty; add +inf
257     {
258       IVT iv; // empty
259       iv.add_point (pos_infinity ());
260       EQUAL (iv.left (), pos_infinity ());
261       EQUAL (iv.right (), pos_infinity ());
262     }
263 
264     // full; add -inf
265     {
266       IVT iv = IVT::longest ();
267       iv.add_point (neg_infinity ());
268       EQUAL (iv.left (), neg_infinity ());
269       EQUAL (iv.right (), pos_infinity ());
270     }
271 
272     // full; add 0
273     {
274       IVT iv = IVT::longest ();
275       iv.add_point (T (0));
276       EQUAL (iv.left (), neg_infinity ());
277       EQUAL (iv.right (), pos_infinity ());
278     }
279 
280     // full; add +inf
281     {
282       IVT iv = IVT::longest ();
283       iv.add_point (pos_infinity ());
284       EQUAL (iv.left (), neg_infinity ());
285       EQUAL (iv.right (), pos_infinity ());
286     }
287 
288     // nonempty, nonfull; add -inf
289     {
290       IVT iv {T (10), T (20)};
291       iv.add_point (neg_infinity ());
292       EQUAL (iv.left (), neg_infinity ());
293       EQUAL (iv.right (), T (20));
294     }
295 
296     // nonempty, nonfull; add point < left
297     {
298       IVT iv {T (10), T (20)};
299       iv.add_point (T (1));
300       EQUAL (iv.left (), T (1));
301       EQUAL (iv.right (), T (20));
302     }
303 
304     // nonempty, nonfull; add point already in interval
305     {
306       IVT iv {T (10), T (20)};
307       iv.add_point ( T (0));
308       EQUAL (iv.left (), T (0));
309       EQUAL (iv.right (), T (20));
310     }
311 
312     // nonempty, nonfull; add point > right
313     {
314       IVT iv {T (10), T (20)};
315       iv.add_point (T (21));
316       EQUAL (iv.left (), T (10));
317       EQUAL (iv.right (), T (21));
318     }
319 
320     // nonempty, nonfull; add +inf
321     {
322       IVT iv {T (10), T (20)};
323       iv.add_point (pos_infinity ());
324       EQUAL (iv.left (), T (10));
325       EQUAL (iv.right (), pos_infinity ());
326     }
327   }
328 
test_empty()329   void test_empty ()
330   {
331     constexpr auto z = T (0);
332     constexpr auto p = pos_infinity ();
333 
334     static_assert (IVT {}.is_empty () == true, "");
335 
336     static_assert (IVT {z}.is_empty () == false, "");
337     static_assert (IVT {p}.is_empty () == false, "");
338 
339     static_assert (IVT {z, z}.is_empty () == false, "");
340     static_assert (IVT {z, p}.is_empty () == false, "");
341     static_assert (IVT {p, z}.is_empty () == true, "");
342     static_assert (IVT {p, p}.is_empty () == false, "");
343 
344     constexpr auto n = neg_infinity ();
345     if (z != n) // more cases for signed types
346       {
347         CHECK (IVT (n).is_empty () == false);
348 
349         CHECK (IVT (n, n).is_empty () == false);
350         CHECK (IVT (n, z).is_empty () == false);
351         CHECK (IVT (n, p).is_empty () == false);
352         CHECK (IVT (z, n).is_empty () == true);
353         CHECK (IVT (p, n).is_empty () == true);
354       }
355   }
356 
test_intersect()357   void test_intersect ()
358   {
359     // empty v. full
360     {
361       IVT iv; // empty
362       iv.intersect (IVT::longest ());
363       // as empty as empty can be
364       EQUAL (iv.left (), pos_infinity ());
365       EQUAL (iv.right (), neg_infinity ());
366     }
367 
368     // empty v. nonempty, nonfull
369     {
370       IVT iv; // empty
371       iv.intersect ({T (12), T (34)});
372       // as empty as empty can be
373       EQUAL (iv.left (), pos_infinity ());
374       EQUAL (iv.right (), neg_infinity ());
375     }
376 
377     // empty v. empty
378     {
379       IVT iv; // empty
380       iv.intersect (IVT ());
381       // as empty as empty can be
382       EQUAL (iv.left (), pos_infinity ());
383       EQUAL (iv.right (), neg_infinity ());
384     }
385 
386     // full v. empty
387     {
388       IVT iv = IVT::longest ();
389       iv.intersect (IVT ());
390       // as empty as empty can be
391       EQUAL (iv.left (), pos_infinity ());
392       EQUAL (iv.right (), neg_infinity ());
393     }
394 
395     // full v. nonempty, nonfull
396     {
397       IVT iv = IVT::longest ();
398       iv.intersect ({T (12), T (34)});
399       EQUAL (iv.left (), T (12));
400       EQUAL (iv.right (), T (34));
401     }
402 
403     // full v. full
404     {
405       IVT iv = IVT::longest ();
406       iv.intersect (IVT::longest ());
407       EQUAL (iv.left (), neg_infinity ());
408       EQUAL (iv.right (), pos_infinity ());
409     }
410 
411     // nonempty, nonfull
412     {
413       IVT iv {T (10), T (20)};
414       iv.intersect ({T (15), T (50)});
415       EQUAL (iv.left (), T (15));
416       EQUAL (iv.right (), T (20));
417     }
418 
419     // reverse, empty -- doesn't need to be specified, but it's nice for
420     // robustness that an empty interval remains empty
421     {
422       IVT iv {T (5), T (-5)};
423       iv.intersect ({});
424       EQUAL (iv.left (), pos_infinity ());
425       EQUAL (iv.right (), neg_infinity ());
426     }
427   }
428 
test_unite()429   void test_unite ()
430   {
431     // empty v. full
432     {
433       IVT iv; // empty
434       iv.unite (IVT::longest ());
435       EQUAL (iv.left (), neg_infinity ());
436       EQUAL (iv.right (), pos_infinity ());
437     }
438 
439     // empty v. nonempty, nonfull
440     {
441       IVT iv; // empty
442       iv.unite ({T (12), T (34)});
443       EQUAL (iv.left (), T (12));
444       EQUAL (iv.right (), T (34));
445     }
446 
447     // empty v. empty
448     {
449       IVT iv; // empty
450       iv.unite (IVT ());
451       // as empty as empty can be
452       EQUAL (iv.left (), pos_infinity ());
453       EQUAL (iv.right (), neg_infinity ());
454     }
455 
456     // full v. empty
457     {
458       IVT iv = IVT::longest ();
459       iv.unite (IVT ());
460       EQUAL (iv.left (), neg_infinity ());
461       EQUAL (iv.right (), pos_infinity ());
462     }
463 
464     // full v. nonempty, nonfull
465     {
466       IVT iv = IVT::longest ();
467       iv.unite ({T (12), T (34)});
468       EQUAL (iv.left (), neg_infinity ());
469       EQUAL (iv.right (), pos_infinity ());
470     }
471 
472     // full v. full
473     {
474       IVT iv = IVT::longest ();
475       iv.unite (IVT::longest ());
476       EQUAL (iv.left (), neg_infinity ());
477       EQUAL (iv.right (), pos_infinity ());
478     }
479 
480     // nonempty, nonfull
481     {
482       IVT iv {T (10), T (20)};
483       iv.unite ({T (15), T (50)});
484       EQUAL (iv.left (), T (10));
485       EQUAL (iv.right (), T (50));
486     }
487 
488     // reverse, full -- doesn't need to be specified, but it's nice for
489     // robustness that a full interval remains full
490     {
491       IVT iv {T (5), T (-5)};
492       iv.unite (IVT::longest ());
493       EQUAL (iv.left (), neg_infinity ());
494       EQUAL (iv.right (), pos_infinity ());
495     }
496   }
497 };
498 
TEST(Interval_math_test<Mint>,add_point_mint)499 TEST (Interval_math_test<Mint>, add_point_mint)
500 {
501   test_add_point ();
502 }
503 
TEST(Interval_math_test<double>,add_point_double)504 TEST (Interval_math_test<double>, add_point_double)
505 {
506   test_add_point ();
507 }
508 
TEST(Interval_math_test<vsize>,add_point_vsize)509 TEST (Interval_math_test<vsize>, add_point_vsize)
510 {
511   test_add_point ();
512 }
513 
TEST(Interval_math_test<Mint>,empty_mint)514 TEST (Interval_math_test<Mint>, empty_mint)
515 {
516   test_empty ();
517 }
518 
TEST(Interval_math_test<double>,empty_double)519 TEST (Interval_math_test<double>, empty_double)
520 {
521   test_empty ();
522 }
523 
TEST(Interval_math_test<vsize>,empty_vsize)524 TEST (Interval_math_test<vsize>, empty_vsize)
525 {
526   test_empty ();
527 }
528 
TEST(Interval_math_test<Mint>,intersect_mint)529 TEST (Interval_math_test<Mint>, intersect_mint)
530 {
531   test_intersect ();
532 }
533 
TEST(Interval_math_test<double>,intersect_double)534 TEST (Interval_math_test<double>, intersect_double)
535 {
536   test_intersect ();
537 }
538 
TEST(Interval_math_test<vsize>,intersect_vsize)539 TEST (Interval_math_test<vsize>, intersect_vsize)
540 {
541   test_intersect ();
542 }
543 
TEST(Interval_math_test<Mint>,unite_mint)544 TEST (Interval_math_test<Mint>, unite_mint)
545 {
546   test_unite ();
547 }
548 
TEST(Interval_math_test<double>,unite_double)549 TEST (Interval_math_test<double>, unite_double)
550 {
551   test_unite ();
552 }
553 
TEST(Interval_math_test<vsize>,unite_vsize)554 TEST (Interval_math_test<vsize>, unite_vsize)
555 {
556   test_unite ();
557 }
558 
TEST(Interval_test,is_empty_double_nan)559 TEST (Interval_test, is_empty_double_nan)
560 {
561   // We can't test with static_assert because GCC does not handle NaN
562   // comparisons consistently.  0 > NaN is constexpr, but NaN > 0 isn't.
563   // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88173
564 
565   {
566     constexpr Interval iv {-NAN, -NAN};
567     CHECK (!iv.is_empty ());
568   }
569 
570   {
571     constexpr Interval iv {-NAN, 0};
572     CHECK (!iv.is_empty ());
573   }
574 
575   {
576     constexpr Interval iv {-NAN, NAN};
577     CHECK (!iv.is_empty ());
578   }
579 
580   {
581     constexpr Interval iv {0, -NAN};
582     CHECK (!iv.is_empty ());
583   }
584 
585   {
586     constexpr Interval iv {0, NAN};
587     CHECK (!iv.is_empty ());
588   }
589 
590   {
591     constexpr Interval iv {NAN, -NAN};
592     CHECK (!iv.is_empty ());
593   }
594 
595   {
596     constexpr Interval iv {NAN, 0};
597     CHECK (!iv.is_empty ());
598   }
599 
600   {
601     constexpr Interval iv {NAN, NAN};
602     CHECK (!iv.is_empty ());
603   }
604 }
605 
TEST(Interval_test,length_double_infinity)606 TEST (Interval_test, length_double_infinity)
607 {
608   {
609     Interval iv {-INFINITY, -INFINITY};
610     CHECK (std::isnan (iv.length ()));
611   }
612 
613   {
614     Interval iv {-INFINITY, 0};
615     EQUAL (iv.length (), INFINITY);
616   }
617 
618   {
619     Interval iv {-INFINITY, INFINITY};
620     EQUAL (iv.length (), INFINITY);
621   }
622 
623   {
624     Interval iv {0, -INFINITY};
625     EQUAL (iv.length (), 0);
626   }
627 
628   {
629     Interval iv {0, INFINITY};
630     EQUAL (iv.length (), INFINITY);
631   }
632 
633   {
634     Interval iv {INFINITY, -INFINITY};
635     EQUAL (iv.length (), 0);
636   }
637 
638   {
639     Interval iv {INFINITY, 0};
640     EQUAL (iv.length (), 0);
641   }
642 
643   {
644     Interval iv {INFINITY, INFINITY};
645     CHECK (std::isnan (iv.length ()));
646   }
647 }
648 
TEST(Interval_test,length_double_nan)649 TEST (Interval_test, length_double_nan)
650 {
651   {
652     Interval iv {0, NAN};
653     CHECK (std::isnan (iv.length ()));
654   }
655 
656   {
657     Interval iv {NAN, 0};
658     CHECK (std::isnan (iv.length ()));
659   }
660 
661   {
662     Interval iv {-NAN, NAN};
663     CHECK (std::isnan (iv.length ()));
664   }
665 
666   {
667     Interval iv {NAN, -NAN};
668     CHECK (std::isnan (iv.length ()));
669   }
670 }
671 
TEST(Interval_test,set_empty)672 TEST (Interval_test, set_empty)
673 {
674   IVM iv {Mint (-33), Mint (33)};
675   iv.set_empty ();
676   EQUAL (iv.left (), Mint (100));
677   EQUAL (iv.right (), Mint (-100));
678 }
679 
TEST(Interval_test,set_full)680 TEST (Interval_test, set_full)
681 {
682   IVM iv {Mint (-33), Mint (33)};
683   iv.set_full ();
684   EQUAL (iv.left (), Mint (-100));
685   EQUAL (iv.right (), Mint (100));
686 }
687 
TEST(Interval_test,convert_to_string)688 TEST (Interval_test, convert_to_string)
689 {
690   {
691     IVM iv {Mint (17), Mint (-1)};
692     EQUAL (to_string (iv), "[empty]");
693   }
694 
695   {
696     IVM iv {Mint (-24), Mint (68)};
697     EQUAL (to_string (iv), "[-24,68]");
698   }
699 }
700