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