SMTStabilizer API
Public API documentation for SMTStabilizer
Loading...
Searching...
No Matches
interval.cpp
Go to the documentation of this file.
1/* -*- Source -*-
2 *
3 * The Interval Source
4 *
5 * Author: Fuqi Jia <jiafq@ios.ac.cn>
6 *
7 * Copyright (C) 2025 Fuqi Jia
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 * DEALINGS IN THE SOFTWARE.
26 */
27// Modified by Xiang Zhang, 2026
28// Additional changes licensed under the MIT License
29#include "interval.h"
30
31#include "asserting.h"
32
33namespace stabilizer::parser {
34
35Interval::Interval(Number lower, Number upper, bool leftClosed, bool rightClosed)
36 : lower(lower), upper(upper), leftClosed(leftClosed), rightClosed(rightClosed) {}
37
39 : lower(other.lower), upper(other.upper), leftClosed(other.leftClosed), rightClosed(other.rightClosed) {}
40
42
44 if (this != &other) {
45 lower = other.lower;
46 upper = other.upper;
47 leftClosed = other.leftClosed;
49 }
50 return *this;
51}
52
53bool Interval::operator==(const Interval &other) const {
54 return lower == other.lower && upper == other.upper &&
55 leftClosed == other.leftClosed && rightClosed == other.rightClosed;
56}
57
58bool Interval::operator!=(const Interval &other) const {
59 return !(*this == other);
60}
61
62void Interval::setLower(const Number &lower) {
63 if (lower > upper) {
64 throw std::invalid_argument("Lower bound is greater than upper bound");
65 }
66 this->lower = lower;
67}
68
69void Interval::setUpper(const Number &upper) {
70 if (upper < lower) {
71 throw std::invalid_argument("Upper bound is less than lower bound");
72 }
73 this->upper = upper;
74}
75
76void Interval::setLeftClosed(bool leftClosed) { this->leftClosed = leftClosed; }
77
78void Interval::setRightClosed(bool rightClosed) {
79 this->rightClosed = rightClosed;
80}
81
82bool Interval::isLeftUnbounded() const { return lower.isInfinity(); }
83
85
86Number Interval::midpoint() const { return (lower + upper) / 2; }
87
88std::string Interval::toString() const {
89 std::string result = "";
90 if (leftClosed) {
91 result += "[";
92 }
93 else {
94 result += "(";
95 }
96 result += lower.toString() + ", " + upper.toString();
97 if (rightClosed) {
98 result += "]";
99 }
100 else {
101 result += ")";
102 }
103 return result;
104}
105
106void Interval::getIntegers(std::vector<Number> &integers) const {
107 if (lower.isInfinity() || upper.isInfinity()) {
108 throw std::invalid_argument("Interval is unbounded");
109 }
110 for (Number i = lower.ceil(); i <= upper.floor(); i++) {
111 integers.push_back(i);
112 }
113}
114
115bool Interval::isPoint() const { return lower == upper; }
116
117bool Interval::isEmpty() const {
118 return lower > upper || (lower == upper && (!leftClosed || !rightClosed));
119}
120
122 if (isEmpty()) {
123 // empty interval
124 return Number(-1);
125 }
126 return upper - lower;
127}
128
129bool Interval::contains(const Number &value) const {
130 if (isEmpty())
131 return false;
132 bool lowerOk = leftClosed ? (lower <= value) : (lower < value);
133 bool upperOk = rightClosed ? (value <= upper) : (value < upper);
134 return lowerOk && upperOk;
135}
136
137bool Interval::isSubsetOf(const Interval &other) const {
138 if (isEmpty())
139 return true;
140 if (other.isEmpty())
141 return false;
142
143 // check lower bound
144 bool lowerOk = (lower > other.lower) ||
145 (lower == other.lower && (!leftClosed || other.leftClosed));
146
147 // check upper bound
148 bool upperOk = (upper < other.upper) ||
149 (upper == other.upper && (!rightClosed || other.rightClosed));
150
151 return lowerOk && upperOk;
152}
153
154bool Interval::isSupersetOf(const Interval &other) const {
155 if (isEmpty()) {
156 return false;
157 }
158 // superset requirement:
159 // 1. A.lower <= B.lower, if A.lower == B.lower, then A.leftClosed >=
160 // B.leftClosed
161 // 2. A.upper >= B.upper, if A.upper == B.upper, then A.rightClosed >=
162 // B.rightClosed
163 bool lowerOk = (lower < other.lower) ||
164 (lower == other.lower && (leftClosed || !other.leftClosed));
165 bool upperOk = (upper > other.upper) ||
166 (upper == other.upper && (rightClosed || !other.rightClosed));
167 return lowerOk && upperOk;
168}
169
170bool Interval::isDisjointFrom(const Interval &other) const {
171 if (isEmpty() || other.isEmpty()) {
172 return false;
173 }
174 // disjoint requirement:
175 // 1. A.upper < B.lower, or
176 // 2. A.upper = B.lower, but at least one is open
177 // 3. B.upper < A.lower, or
178 // 4. B.upper = A.lower, but at least one is open
179 return upper < other.lower ||
180 (upper == other.lower && (!rightClosed || !other.leftClosed)) ||
181 lower > other.upper ||
182 (lower == other.upper && (!leftClosed || !other.rightClosed));
183}
184
185bool Interval::isIntersectingWith(const Interval &other) const {
186 if (isEmpty() || other.isEmpty()) {
187 return false;
188 }
189 // intersecting requirement: not disjoint
190 return !isDisjointFrom(other);
191}
192
194 if (isEmpty() || other.isEmpty()) {
195 return EmptyInterval;
196 }
197 Number low = std::max(lower, other.lower);
198 Number high = std::min(upper, other.upper);
199 bool newLeftClosed = (lower < other.lower) ? other.leftClosed
200 : (lower > other.lower) ? leftClosed
201 : leftClosed && other.leftClosed;
202 bool newRightClosed = (upper > other.upper) ? other.rightClosed
203 : (upper < other.upper)
205 : rightClosed && other.rightClosed;
206 return Interval(low, high, newLeftClosed, newRightClosed);
207}
208
210 if (isEmpty() || other.isEmpty()) {
211 return EmptyInterval;
212 }
213 if (isDisjointFrom(other)) {
214 throw std::invalid_argument("Intervals are disjoint");
215 }
216
217 // take the smaller lower bound, if equal, take the or of the left closedness
218 Number newLower = std::min(lower, other.lower);
219 bool newLeftClosed = (lower < other.lower) ? leftClosed
220 : (other.lower < lower)
221 ? other.leftClosed
222 : (leftClosed || other.leftClosed);
223
224 // take the larger upper bound, if equal, take the or of the right closedness
225 Number newUpper = std::max(upper, other.upper);
226 bool newRightClosed = (upper > other.upper) ? rightClosed
227 : (other.upper > upper)
228 ? other.rightClosed
229 : (rightClosed || other.rightClosed);
230
231 return Interval(newLower, newUpper, newLeftClosed, newRightClosed);
232}
233
234bool Interval::isSubsetEqOf(const Interval &other) const {
235 if (isEmpty()) {
236 return true;
237 }
238 // subset requirement: all elements in A are in B
239 return lower >= other.lower && upper <= other.upper &&
240 (other.leftClosed || lower > other.lower || !leftClosed) &&
241 (other.rightClosed || upper < other.upper || !rightClosed);
242}
243
245 if (isEmpty())
246 return 0;
247 if (isPoint() && lower.isInteger())
248 return 1;
249
250 if (leftClosed && rightClosed) {
251 return static_cast<size_t>(
252 (upper.floor() - lower.ceil() + Number(1)).toInteger().toULong());
253 }
254 else if (leftClosed && !rightClosed) {
255 return static_cast<size_t>(
256 (upper.floor() - lower.ceil()).toInteger().toULong());
257 }
258 else if (!leftClosed && rightClosed) {
259 return static_cast<size_t>(
260 (upper.floor() - lower.ceil()).toInteger().toULong());
261 }
262 else {
263 // open interval
264 return static_cast<size_t>(
265 (upper.floor() - lower.ceil() - Number(1)).toInteger().toULong());
266 }
267}
268
269std::vector<Interval> Interval::difference(const Interval &other) const {
270 // if the intervals are disjoint, return A
271 if (isDisjointFrom(other)) {
272 return {*this};
273 }
274
275 // if B is completely contained in A, return empty set
276 if (isSupersetOf(other)) {
277 return {};
278 }
279
280 std::vector<Interval> result;
281
282 // if B is inside A, split A into two intervals
283 if (isSubsetOf(other)) {
284 // left interval: [a.low, b.low)
285 bool rightClosed = !other.leftClosed;
286 result.push_back(Interval(lower, other.lower, leftClosed, rightClosed));
287
288 // right interval: (b.high, a.high]
289 bool leftClosed = !other.rightClosed;
290 result.push_back(Interval(other.upper, upper, leftClosed, rightClosed));
291
292 return result;
293 }
294
295 // if B covers part of A from the left
296 if ((other.lower <= lower ||
297 (other.lower == lower && other.leftClosed >= leftClosed)) &&
298 (other.upper < upper ||
299 (other.upper == upper && !other.rightClosed && rightClosed))) {
300 // right remaining part: (b.high, a.high]
301 bool leftClosed = !other.rightClosed;
302 result.push_back(Interval(other.upper, upper, leftClosed, rightClosed));
303
304 return result;
305 }
306
307 // if B covers part of A from the right
308 if ((other.lower > lower ||
309 (other.lower == lower && !other.leftClosed && leftClosed)) &&
310 (other.upper >= upper ||
311 (other.upper == upper && other.rightClosed >= rightClosed))) {
312 // left remaining part: [a.low, b.low)
313 bool rightClosed = !other.leftClosed;
314 result.push_back(Interval(lower, other.lower, leftClosed, rightClosed));
315
316 return result;
317 }
318
319 // theoretically should not reach here, but return empty set for safety
320 return {};
321}
322
323std::vector<Interval>
324Interval::unionMulti(const std::vector<Interval> &intervals) {
325 if (intervals.empty())
326 return {};
327 if (intervals.size() == 1)
328 return {intervals[0]};
329
330 // sort the intervals by the lower bound
331 std::vector<Interval> sortedIntervals = intervals;
332 std::sort(sortedIntervals.begin(), sortedIntervals.end(), [](const Interval &a, const Interval &b) {
333 return a.lower < b.lower ||
334 (a.lower == b.lower && a.leftClosed > b.leftClosed);
335 });
336
337 std::vector<Interval> result;
338 Interval current = sortedIntervals[0];
339
340 for (size_t i = 1; i < sortedIntervals.size(); ++i) {
341 // check if the current interval intersects with the next interval
342 bool intersectOrAdjacent = !current.isDisjointFrom(sortedIntervals[i]);
343
344 if (intersectOrAdjacent) {
345 // if they intersect or are adjacent, merge the intervals
346 current = Interval(
347 std::min(current.lower, sortedIntervals[i].lower),
348 std::max(current.upper, sortedIntervals[i].upper),
349 (current.lower < sortedIntervals[i].lower) ? current.leftClosed
350 : (sortedIntervals[i].lower < current.lower)
351 ? sortedIntervals[i].leftClosed
352 : (current.leftClosed || sortedIntervals[i].leftClosed),
353 (current.upper > sortedIntervals[i].upper) ? current.rightClosed
354 : (sortedIntervals[i].upper > current.upper)
355 ? sortedIntervals[i].rightClosed
356 : (current.rightClosed || sortedIntervals[i].rightClosed));
357 }
358 else {
359 // if they are disjoint, add the current interval to the result, and
360 // update the current interval to the next interval
361 result.push_back(current);
362 current = sortedIntervals[i];
363 }
364 }
365
366 // add the last interval
367 result.push_back(current);
368
369 return result;
370}
371
373 if (isEmpty()) {
374 return EmptyInterval;
375 }
376 return Interval(lower + 1, upper + 1, leftClosed, rightClosed);
377}
379 if (isEmpty()) {
380 return EmptyInterval;
381 }
382 return Interval(lower - 1, upper - 1, leftClosed, rightClosed);
383}
385 if (isEmpty()) {
386 return EmptyInterval;
387 }
389}
391 if (isEmpty()) {
392 return EmptyInterval;
393 }
394 return *this;
395}
397 if (isEmpty()) {
398 return EmptyInterval;
399 }
401}
403 if (isEmpty()) {
404 return EmptyInterval;
405 }
407}
409 if (isEmpty()) {
410 return EmptyInterval;
411 }
412 return Interval(lower + 1, upper + 1, leftClosed, rightClosed);
413}
415 if (isEmpty()) {
416 return EmptyInterval;
417 }
418 return Interval(lower - 1, upper - 1, leftClosed, rightClosed);
419}
421 if (isEmpty()) {
422 return EmptyInterval;
423 }
425}
427 if (isEmpty()) {
428 return EmptyInterval;
429 }
430 if (lower >= 0) {
431 // [a,b] with a ≥ 0 -> [a,b]
432 return *this;
433 }
434 else if (upper <= 0) {
435 // [a,b] with b ≤ 0 -> [-b,-a]
437 }
438 else {
439 // [a,b] with a < 0 < b -> [0,max(|a|,|b|)]
440 Number maxAbs = std::max(lower.abs(), upper.abs());
441 return Interval(Number(0), maxAbs, true, (maxAbs == lower.abs()) ? leftClosed : rightClosed);
442 }
443}
445 // log base 2
446 if (isEmpty()) {
447 return EmptyInterval;
448 }
449
450 // lb(x) is defined for x > 0
451 if (upper <= 0) {
452 return EmptyInterval;
453 }
454
455 Number new_lower = 0;
456 Number new_upper = 0;
457 bool new_leftClosed = true;
458 bool new_rightClosed = true;
459 if (upper <= 0) {
460 return EmptyInterval;
461 }
462
463 // for lower bound
464 if (lower <= 0) {
465 new_lower = Number::negativeInfinity();
466 new_leftClosed = false;
467 }
468 else if (lower.isPositiveInfinity()) {
469 new_lower = Number::positiveInfinity();
470 new_leftClosed = false;
471 }
472 else {
473 new_lower = lower.lb();
474 new_lower = new_lower.nextBelow();
475 new_leftClosed = false;
476 }
477
478 // for upper bound
480 new_upper = Number::positiveInfinity();
481 new_rightClosed = false;
482 }
483 else {
484 new_upper = upper.lb();
485 new_upper = new_upper.nextAbove();
486 new_rightClosed = false;
487 }
488
489 return Interval(new_lower, new_upper, new_leftClosed, new_rightClosed);
490}
492 if (isEmpty()) {
493 return EmptyInterval;
494 }
495
496 // ln(x) is defined for x > 0
497 if (upper <= 0) {
498 return EmptyInterval;
499 }
500
501 Number new_lower = 0;
502 Number new_upper = 0;
503 bool new_leftClosed = true;
504 bool new_rightClosed = true;
505
506 // for lower bound
507 if (lower <= 0) {
508 new_lower = Number::negativeInfinity();
509 new_leftClosed = false;
510 }
511 else if (lower.isPositiveInfinity()) {
512 new_lower = Number::positiveInfinity();
513 new_leftClosed = false;
514 }
515 else {
516 new_lower = lower.ln();
517 new_lower = new_lower.nextBelow();
518 new_leftClosed = false;
519 }
520
521 // for upper bound
523 new_upper = Number::positiveInfinity();
524 new_rightClosed = false;
525 }
526 else {
527 new_upper = upper.ln();
528 new_upper = new_upper.nextAbove();
529 new_rightClosed = false;
530 }
531
532 return Interval(new_lower, new_upper, new_leftClosed, new_rightClosed);
533}
535 if (isEmpty()) {
536 return EmptyInterval;
537 }
538
539 // lg(x) is defined for x > 0
540 if (upper <= 0) {
541 return EmptyInterval;
542 }
543
544 Number new_lower = 0;
545 Number new_upper = 0;
546 bool new_leftClosed = true;
547 bool new_rightClosed = true;
548
549 // for lower bound
550 if (lower <= 0) {
551 new_lower = Number::negativeInfinity();
552 new_leftClosed = false;
553 }
554 else if (lower.isPositiveInfinity()) {
555 new_lower = Number::positiveInfinity();
556 new_leftClosed = false;
557 }
558 else {
559 new_lower = lower.lg();
560 new_lower = new_lower.nextBelow();
561 new_leftClosed = false;
562 }
563
564 // for upper bound
566 new_upper = Number::positiveInfinity();
567 new_rightClosed = false;
568 }
569 else {
570 new_upper = upper.lg();
571 new_upper = new_upper.nextAbove();
572 new_rightClosed = false;
573 }
574
575 return Interval(new_lower, new_upper, new_leftClosed, new_rightClosed);
576}
578 if (isEmpty()) {
579 return EmptyInterval;
580 }
581
582 Number newLower = lower;
583 Number newUpper = upper;
584 bool newLeftClosed = false;
585 bool newRightClosed = false;
587 // exp(-inf) = 0
588 newLower = Number(0);
589 }
590 else if (lower.isPositiveInfinity()) {
591 newLower = Number::positiveInfinity();
592 }
593 else {
594 newLower = lower.exp();
595 newLower = newLower.nextBelow();
596 }
597
599 newUpper = Number(0);
600 }
601 else if (upper.isPositiveInfinity()) {
602 newUpper = Number::positiveInfinity();
603 }
604 else {
605 newUpper = upper.exp();
606 newUpper = newUpper.nextAbove();
607 }
608
609 return Interval(newLower, newUpper, newLeftClosed, newRightClosed);
610}
611
613 if (isEmpty()) {
614 return EmptyInterval;
615 }
616 if (upper <= 0) {
617 return EmptyInterval;
618 }
619 Number newLower = lower;
620 Number newUpper = upper;
621 bool newLeftClosed = leftClosed;
622 bool newRightClosed = rightClosed;
623 if (lower <= 0) {
624 newLower = Number(0);
625 newLeftClosed = true;
626 }
627 else if (lower.isPositiveInfinity()) {
628 newLower = Number::positiveInfinity();
629 newLeftClosed = false;
630 }
631 else {
632 newLower = lower.sqrt();
633 newLower = newLower.nextBelow();
634 newLeftClosed = false;
635 }
636
637 if (upper <= 0) {
638 newUpper = Number(0);
639 newRightClosed = true;
640 }
641 else if (upper.isPositiveInfinity()) {
642 newUpper = Number::positiveInfinity();
643 newRightClosed = false;
644 }
645 else {
646 newUpper = upper.sqrt();
647 newUpper = newUpper.nextAbove();
648 newRightClosed = false;
649 }
650
651 return Interval(newLower, newUpper, newLeftClosed, newRightClosed);
652}
653
655 if (isEmpty()) {
656 return EmptyInterval;
657 }
658
659 if (upper <= 0) {
660 return EmptyInterval;
661 }
662 Number newLower = lower;
663 Number newUpper = upper;
664 bool newLeftClosed = leftClosed;
665 bool newRightClosed = rightClosed;
666 if (lower <= 0) {
667 newLower = Number(0);
668 newLeftClosed = true;
669 }
670 else if (lower.isPositiveInfinity()) {
671 newLower = Number::positiveInfinity();
672 newLeftClosed = false;
673 }
674 else {
675 newLower = lower.safeSqrt();
676 newLower = newLower.nextBelow();
677 newLeftClosed = false;
678 }
679
680 if (upper <= 0) {
681 newUpper = Number(0);
682 newRightClosed = true;
683 }
684 else if (upper.isPositiveInfinity()) {
685 newUpper = Number::positiveInfinity();
686 newRightClosed = false;
687 }
688 else {
689 newUpper = upper.safeSqrt();
690 newUpper = newUpper.nextAbove();
691 newRightClosed = false;
692 }
693
694 return Interval(newLower, newUpper, newLeftClosed, newRightClosed);
695}
696
698 if (isEmpty()) {
699 return EmptyInterval;
700 }
701
702 // define the period
703 const Number TWO_PI = Number(2) * Number::pi();
704
705 // if the interval contains infinity, the result is undefined
706 if (lower.isInfinity() || upper.isInfinity()) {
707 return Interval(Number(-1), Number(1), true, true);
708 }
709
710 // check if the interval covers the whole period
711 if ((upper - lower) >= TWO_PI) {
712 return Interval(Number(-1), Number(1), true, true); // full range [-1, 1]
713 }
714
715 // normalize to [0, 2Ï€)
716 Number factor = (lower / TWO_PI).floor();
717 Number lowNorm = lower - factor * TWO_PI;
718 if (lowNorm < Number(0))
719 lowNorm += TWO_PI;
720
721 factor = (upper / TWO_PI).floor();
722 Number highNorm = upper - factor * TWO_PI;
723 if (highNorm < Number(0))
724 highNorm += TWO_PI;
725
726 // if the interval crosses the period boundary
727 if (highNorm < lowNorm) {
728 highNorm += TWO_PI;
729 }
730
731 // calculate the endpoint values
732 Number sinLow = lowNorm.sin();
733 Number sinHigh = highNorm.sin();
734
735 Number minVal = std::min(sinLow, sinHigh);
736 Number maxVal = std::max(sinLow, sinHigh);
737 bool newLeftClosed = sinLow < sinHigh ? leftClosed : rightClosed;
738 bool newRightClosed = sinLow < sinHigh ? rightClosed : leftClosed;
739
740 // check if the interval passes the extreme points π/2 or 3π/2
741 Number PI_HALF = Number::pi() / Number(2);
742 Number THREE_PI_HALF = Number(3) * Number::pi() / Number(2);
743
744 // check if the interval contains π/2 (sine maximum is 1)
745 if ((lowNorm <= PI_HALF && highNorm >= PI_HALF) ||
746 (lowNorm <= PI_HALF + TWO_PI && highNorm >= PI_HALF + TWO_PI)) {
747 maxVal = Number(1);
748 newLeftClosed = true;
749 }
750
751 // check if the interval contains 3Ï€/2 (sine minimum is -1)
752 if ((lowNorm <= THREE_PI_HALF && highNorm >= THREE_PI_HALF) ||
753 (lowNorm <= THREE_PI_HALF + TWO_PI &&
754 highNorm >= THREE_PI_HALF + TWO_PI)) {
755 minVal = Number(-1);
756 newRightClosed = true;
757 }
758
759 if (minVal > Number(-1)) {
760 // extend a small value
761 minVal = minVal.nextBelow();
762 newLeftClosed = false;
763 }
764 if (maxVal < Number(1)) {
765 // extend a small value
766 maxVal = maxVal.nextAbove();
767 newRightClosed = false;
768 }
769
770 return Interval(minVal, maxVal, newLeftClosed, newRightClosed);
771}
772
774 if (isEmpty()) {
775 return EmptyInterval;
776 }
777
778 // define the period
779 const Number TWO_PI = Number(2) * Number::pi();
780
781 // if the interval contains infinity, the result is undefined
782 if (lower.isInfinity() || upper.isInfinity()) {
783 return Interval(Number(-1), Number(1), true, true);
784 }
785
786 // check if the interval covers the whole period
787 if ((upper - lower) >= TWO_PI) {
788 return Interval(Number(-1), Number(1), true, true); // full range [-1, 1]
789 }
790
791 // normalize to [0, 2Ï€)
792 Number factor = (lower / TWO_PI).floor();
793 Number lowNorm = lower - factor * TWO_PI;
794 if (lowNorm < Number(0))
795 lowNorm += TWO_PI;
796
797 factor = (upper / TWO_PI).floor();
798 Number highNorm = upper - factor * TWO_PI;
799 if (highNorm < Number(0))
800 highNorm += TWO_PI;
801
802 // if the interval crosses the period boundary
803 if (highNorm < lowNorm) {
804 highNorm += TWO_PI;
805 }
806
807 // calculate the endpoint values
808 Number cosLow = lowNorm.cos();
809 Number cosHigh = highNorm.cos();
810
811 Number minVal = std::min(cosLow, cosHigh);
812 Number maxVal = std::max(cosLow, cosHigh);
813
814 bool newLeftClosed = cosLow < cosHigh ? leftClosed : rightClosed;
815 bool newRightClosed = cosLow < cosHigh ? rightClosed : leftClosed;
816
817 // check if the interval passes the extreme points 0 or π
818 Number ZERO = Number(0);
819 Number PI = Number::pi();
820
821 // check if the interval contains 0 (cosine maximum is 1)
822 if ((lowNorm <= ZERO && highNorm >= ZERO) ||
823 (lowNorm <= ZERO + TWO_PI && highNorm >= ZERO + TWO_PI)) {
824 maxVal = Number(1);
825 newLeftClosed = true;
826 }
827
828 // check if the interval contains π (cosine minimum is -1)
829 if ((lowNorm <= PI && highNorm >= PI) ||
830 (lowNorm <= PI + TWO_PI && highNorm >= PI + TWO_PI)) {
831 minVal = Number(-1);
832 newRightClosed = true;
833 }
834
835 if (minVal > Number(-1)) {
836 // extend a small value
837 minVal = minVal.nextBelow();
838 newLeftClosed = false;
839 }
840 if (maxVal < Number(1)) {
841 // extend a small value
842 maxVal = maxVal.nextAbove();
843 newRightClosed = false;
844 }
845
846 return Interval(minVal, maxVal, newLeftClosed, newRightClosed);
847}
848
850 if (isEmpty()) {
851 return EmptyInterval;
852 }
853
854 // define the constants
855 const Number PI_HALF = Number::pi() / Number(2);
856 const Number PI = Number::pi();
857
858 // if the interval contains infinity, the result is undefined
859 if (lower.isInfinity() || upper.isInfinity()) {
860 return FullInterval;
861 }
862
863 // normalize to [-π/2, π/2)
864 Number factor = (lower / PI).floor();
865 Number lowMod = lower - factor * PI;
866 if (lowMod < -PI_HALF)
867 lowMod += PI;
868 if (lowMod >= PI_HALF)
869 lowMod -= PI;
870
871 factor = (upper / PI).floor();
872 Number highMod = upper - factor * PI;
873 if (highMod < -PI_HALF)
874 highMod += PI;
875 if (highMod >= PI_HALF)
876 highMod -= PI;
877
878 // check if the interval contains odd multiples of π/2 (tangent singularities)
879 if ((lowMod <= -PI_HALF && highMod >= -PI_HALF) ||
880 (lowMod <= PI_HALF && highMod >= PI_HALF)) {
881 // return infinite interval (tangent is unbounded)
883 }
884
885 // calculate the tangent values at the endpoints
886 Number tanLow = lower.tan();
887 Number tanHigh = upper.tan();
888
889 bool newLeftClosed = leftClosed;
890 bool newRightClosed = rightClosed;
891
892 if (tanLow > tanHigh) {
893 std::swap(tanLow, tanHigh);
894 std::swap(newLeftClosed, newRightClosed);
895 }
896 // extend a small value
897 tanLow = tanLow.nextBelow();
898 tanHigh = tanHigh.nextAbove();
899 newLeftClosed = false;
900 newRightClosed = false;
901
902 return Interval(tanLow, tanHigh, newLeftClosed, newRightClosed);
903}
904
906 if (isEmpty()) {
907 return EmptyInterval;
908 }
909
910 // cotangent function is the reciprocal of tangent function
911 Interval tanInterval = this->tan();
912
913 // if the tan interval contains 0, the cotangent function has singularities
914 if (tanInterval.contains(Number(0))) {
916 }
917
918 // cotangent function cot(x) = 1/tan(x)
919 Interval one = Interval(Number(1), Number(1), true, true);
920 return one / tanInterval;
921}
922
924 if (isEmpty()) {
925 return EmptyInterval;
926 }
927
928 // secant function is the reciprocal of cosine function
929 Interval cosInterval = this->cos();
930
931 // if the cos interval contains 0, the secant function has singularities
932 if (cosInterval.contains(Number(0))) {
934 }
935
936 // secant function sec(x) = 1/cos(x)
937 Interval one = Interval(Number(1), Number(1), true, true);
938 return one / cosInterval;
939}
940
942 if (isEmpty()) {
943 return EmptyInterval;
944 }
945
946 // cosecant function is the reciprocal of sine function
947 Interval sinInterval = this->sin();
948
949 // if the sin interval contains 0, the cosecant function has singularities
950 if (sinInterval.contains(Number(0))) {
952 }
953
954 // cosecant function csc(x) = 1/sin(x)
955 Interval one = Interval(Number(1), Number(1), true, true);
956 return one / sinInterval;
957}
958
960 if (isEmpty()) {
961 return EmptyInterval;
962 }
963
964 // asin function domain is [-1, 1]
965 if (lower < Number(-1) || upper > Number(1)) {
966 throw std::domain_error("Argument for asin must be in range [-1, 1]");
967 }
968
969 // asin function is monotonically increasing
970 Number asinLow = lower.asin();
971 Number asinHigh = upper.asin();
972
973 // add a small value
974 asinLow = asinLow.nextBelow();
975 asinHigh = asinHigh.nextAbove();
976 bool newLeftClosed = false;
977 bool newRightClosed = false;
978
979 return Interval(asinLow, asinHigh, newLeftClosed, newRightClosed);
980}
981
983 if (isEmpty()) {
984 return EmptyInterval;
985 }
986
987 // acos function domain is [-1, 1]
988 if (lower < Number(-1) || upper > Number(1)) {
989 throw std::domain_error("Argument for acos must be in range [-1, 1]");
990 }
991
992 // acos function is monotonically decreasing
993 Number acosLow = upper.acos();
994 Number acosHigh = lower.acos();
995
996 return Interval(acosLow, acosHigh, rightClosed, leftClosed);
997}
998
1000 if (isEmpty()) {
1001 return EmptyInterval;
1002 }
1003
1004 // atan function is monotonically increasing, domain is the whole real number
1005 // set
1006 Number atanLow = lower.atan();
1007 Number atanHigh = upper.atan();
1008
1009 // add a small value
1010 atanLow = atanLow.nextBelow();
1011 atanHigh = atanHigh.nextAbove();
1012 bool newLeftClosed = false;
1013 bool newRightClosed = false;
1014
1015 return Interval(atanLow, atanHigh, newLeftClosed, newRightClosed);
1016}
1017
1019 if (isEmpty()) {
1020 return EmptyInterval;
1021 }
1022
1023 // acot function is monotonically decreasing, domain is the whole real number
1024 // set acot(x) = π/2 - atan(x)
1025 Number acotLow = Number::pi() / Number(2) - upper.atan();
1026 Number acotHigh = Number::pi() / Number(2) - lower.atan();
1027
1028 // added a small value in atan, so no need to add a small value here
1029 return Interval(acotLow, acotHigh, false, false);
1030}
1031
1033 if (isEmpty()) {
1034 return EmptyInterval;
1035 }
1036
1037 // asec function domain is (-∞, -1] ∪ [1, ∞)
1038 if (lower > Number(-1) && upper < Number(1)) {
1039 throw std::domain_error("Argument for asec must be outside range (-1, 1)");
1040 }
1041
1042 // handle special case: interval crosses -1 or 1
1043 if (lower <= Number(-1) && upper >= Number(1)) {
1044 // return possible entire value range
1045 return Interval(Number(0), Number::pi(), true, true);
1046 }
1047
1048 // asec function is monotonically increasing
1049 // asec(x) = acos(1/x)
1050 Interval one = Interval(Number(1), Number(1), true, true);
1051 Interval oneOver = one / *this;
1052 return oneOver.acos();
1053}
1054
1056 if (isEmpty()) {
1057 return EmptyInterval;
1058 }
1059
1060 // acsc function domain is (-∞, -1] ∪ [1, ∞)
1061 if (lower > Number(-1) && upper < Number(1)) {
1062 throw std::domain_error("Argument for acsc must be outside range (-1, 1)");
1063 }
1064
1065 // handle special case: interval crosses -1 or 1
1066 if (lower <= Number(-1) && upper >= Number(1)) {
1067 // return possible entire value range
1068 return Interval(-Number::pi() / Number(2), Number::pi() / Number(2), true, true);
1069 }
1070
1071 // acsc function is monotonically decreasing
1072 // acsc(x) = asin(1/x)
1073 Interval one = Interval(Number(1), Number(1), true, true);
1074 Interval oneOver = one / *this;
1075 return oneOver.asin();
1076}
1077
1079 if (isEmpty()) {
1080 return EmptyInterval;
1081 }
1082
1083 // sinh function is monotonically increasing
1084 Number sinhLow = lower.sinh();
1085 Number sinhHigh = upper.sinh();
1086
1087 // add a small value
1088 sinhLow = sinhLow.nextBelow();
1089 sinhHigh = sinhHigh.nextAbove();
1090 bool newLeftClosed = false;
1091 bool newRightClosed = false;
1092
1093 return Interval(sinhLow, sinhHigh, newLeftClosed, newRightClosed);
1094}
1095
1097 if (isEmpty()) {
1098 return EmptyInterval;
1099 }
1100
1101 // cosh function is a even function, the minimum value is 1 at x=0
1102 if (lower >= Number(0)) {
1103 // all the parameters are positive
1104 Number coshLow = lower.cosh();
1105 Number coshHigh = upper.cosh();
1106 bool newLeftClosed = false;
1107 bool newRightClosed = false;
1108
1109 // add a small value
1110 coshLow = coshLow.nextBelow();
1111 coshHigh = coshHigh.nextAbove();
1112
1113 // ensure the value is not less than 1 (the minimum value of cosh)
1114 if (coshLow < Number(1)) {
1115 coshLow = Number(1);
1116 newLeftClosed = true;
1117 }
1118
1119 return Interval(coshLow, coshHigh, newLeftClosed, newRightClosed);
1120 }
1121 else if (upper <= Number(0)) {
1122 // all the parameters are negative
1123 Number coshLow = upper.cosh(); // note: since cosh is an even function, we
1124 // swap the order here
1125 Number coshHigh = lower.cosh();
1126 bool newLeftClosed = false;
1127 bool newRightClosed = false;
1128
1129 // add a small value
1130 coshLow = coshLow.nextBelow();
1131 coshHigh = coshHigh.nextAbove();
1132
1133 // ensure the value is not less than 1 (the minimum value of cosh)
1134 if (coshLow < Number(1)) {
1135 coshLow = Number(1);
1136 newLeftClosed = true;
1137 }
1138
1139 return Interval(coshLow, coshHigh, newLeftClosed, newRightClosed);
1140 }
1141 else {
1142 // the interval crosses zero, the minimum value is cosh(0) = 1
1143 Number coshLow = Number(1);
1144 Number coshHigh = std::max(lower.cosh(), upper.cosh());
1145 bool newLeftClosed = true;
1146 bool newRightClosed = false;
1147
1148 // add a small value
1149 coshHigh = coshHigh.nextAbove();
1150
1151 return Interval(coshLow, coshHigh, newLeftClosed, newRightClosed);
1152 }
1153}
1154
1156 if (isEmpty()) {
1157 return EmptyInterval;
1158 }
1159
1160 // tanh function is monotonically increasing, value range is (-1, 1)
1161 Number tanhLow = lower.tanh();
1162 Number tanhHigh = upper.tanh();
1163
1164 // add a small value
1165 tanhLow = tanhLow.nextBelow();
1166 tanhHigh = tanhHigh.nextAbove();
1167 bool newLeftClosed = false;
1168 bool newRightClosed = false;
1169
1170 return Interval(tanhLow, tanhHigh, newLeftClosed, newRightClosed);
1171}
1172
1174 if (isEmpty()) {
1175 return EmptyInterval;
1176 }
1177
1178 // coth function has singularities at 0
1179 if (lower <= Number(0) && upper >= Number(0)) {
1181 }
1182
1183 // coth function coth(x) = 1/tanh(x)
1184 Number cothLow = Number(1) / upper.tanh();
1185 Number cothHigh = Number(1) / lower.tanh();
1186
1187 // added a small value in tanh, so no need to add a small value here
1188 return Interval(cothLow, cothHigh, false, false);
1189}
1190
1192 if (isEmpty()) {
1193 return EmptyInterval;
1194 }
1195
1196 // sech function sech(x) = 1/cosh(x)
1197 Interval one = Interval(Number(1), Number(1), true, true);
1198 Interval coshInterval = one / this->cosh();
1199
1200 // sech function value range is (0, 1], maximum value is 1 at 0
1201 Number sechLow = coshInterval.getLower();
1202 Number sechHigh = coshInterval.getUpper();
1203 bool newLeftClosed = false;
1204 bool newRightClosed = false;
1205
1206 if (coshInterval.getUpper() >= Number(1)) {
1207 sechHigh = Number(1);
1208 newRightClosed = true;
1209 }
1210
1211 if (coshInterval.getLower() <= Number(0)) {
1212 sechLow = Number(0);
1213 }
1214
1215 return Interval(sechLow, sechHigh, newLeftClosed, newRightClosed);
1216}
1217
1219 if (isEmpty()) {
1220 return EmptyInterval;
1221 }
1222
1223 // csch function has singularities at 0
1224 if (lower <= Number(0) && upper >= Number(0)) {
1226 }
1227
1228 // csch function csch(x) = 1/sinh(x)
1229 Interval one = Interval(Number(1), Number(1), true, true);
1230 Interval sinhInterval = one / this->sinh();
1231
1232 // csch function value range is (-∞, 0) ∪ (0, ∞)
1233 Number cschLow = sinhInterval.getLower();
1234 Number cschHigh = sinhInterval.getUpper();
1235 bool newLeftClosed = false;
1236 bool newRightClosed = false;
1237
1238 return Interval(cschLow, cschHigh, newLeftClosed, newRightClosed);
1239}
1240
1242 if (isEmpty()) {
1243 return EmptyInterval;
1244 }
1245
1246 // asinh function is monotonically increasing, domain is the whole real number
1247 // set
1248 Number asinhLow = lower.asinh();
1249 Number asinhHigh = upper.asinh();
1250
1251 // add a small value
1252 asinhLow = asinhLow.nextBelow();
1253 asinhHigh = asinhHigh.nextAbove();
1254 bool newLeftClosed = false;
1255 bool newRightClosed = false;
1256
1257 return Interval(asinhLow, asinhHigh, newLeftClosed, newRightClosed);
1258}
1259
1261 if (isEmpty()) {
1262 return EmptyInterval;
1263 }
1264
1265 // acosh function domain is [1, ∞)
1266 if (lower < Number(1)) {
1267 throw std::domain_error("Argument for acosh must be >= 1");
1268 }
1269
1270 // acosh function is monotonically increasing
1271 Number acoshLow = lower.acosh();
1272 Number acoshHigh = upper.acosh();
1273
1274 // add a small value
1275 acoshLow = acoshLow.nextBelow();
1276 acoshHigh = acoshHigh.nextAbove();
1277 bool newLeftClosed = false;
1278 bool newRightClosed = false;
1279
1280 return Interval(acoshLow, acoshHigh, newLeftClosed, newRightClosed);
1281}
1282
1284 if (isEmpty()) {
1285 return EmptyInterval;
1286 }
1287
1288 // atanh function domain is (-1, 1)
1289 if (lower <= Number(-1) || upper >= Number(1)) {
1290 throw std::domain_error("Argument for atanh must be in range (-1, 1)");
1291 }
1292
1293 // atanh function is monotonically increasing
1294 Number atanhLow = lower.atanh();
1295 Number atanhHigh = upper.atanh();
1296
1297 // add a small value
1298 atanhLow = atanhLow.nextBelow();
1299 atanhHigh = atanhHigh.nextAbove();
1300 bool newLeftClosed = false;
1301 bool newRightClosed = false;
1302
1303 return Interval(atanhLow, atanhHigh, newLeftClosed, newRightClosed);
1304}
1305
1307 if (isEmpty()) {
1308 return EmptyInterval;
1309 }
1310
1311 // acoth function domain is (-∞, -1) ∪ (1, ∞)
1312 if (lower >= Number(-1) && upper <= Number(1)) {
1313 throw std::domain_error("Argument for acoth must be outside range [-1, 1]");
1314 }
1315
1316 // acoth function acoth(x) = atanh(1/x)
1317 Interval one = Interval(Number(1), Number(1), true, true);
1318 Interval oneOver = one / *this;
1319 return oneOver.atanh();
1320}
1321
1323 if (isEmpty()) {
1324 return EmptyInterval;
1325 }
1326
1327 // asech function domain is (0, 1]
1328 if (lower <= Number(0) || upper > Number(1)) {
1329 throw std::domain_error("Argument for asech must be in range (0, 1]");
1330 }
1331
1332 // asech function is monotonically decreasing
1333 // asech(x) = acosh(1/x)
1334 Interval one = Interval(Number(1), Number(1), true, true);
1335 Interval oneOver = one / *this;
1336 return oneOver.acosh();
1337}
1338
1340 if (isEmpty()) {
1341 return EmptyInterval;
1342 }
1343
1344 // acsch function domain is R\{0}
1345 if (lower <= Number(0) && upper >= Number(0)) {
1346 throw std::domain_error("Argument for acsch cannot be 0");
1347 }
1348
1349 // acsch function is monotonically decreasing
1350 // acsch(x) = asinh(1/x)
1351 Interval one = Interval(Number(1), Number(1), true, true);
1352 Interval oneOver = one / *this;
1353 return oneOver.asinh();
1354}
1355
1357 return Interval(lower + value, upper + value, leftClosed, rightClosed);
1358}
1359
1361 return Interval(lower - value, upper - value, leftClosed, rightClosed);
1362}
1363
1365 if (isEmpty()) {
1366 return EmptyInterval;
1367 }
1368 if (value > 0) {
1369 return Interval(lower * value, upper * value, leftClosed, rightClosed);
1370 }
1371 else if (value < 0) {
1372 return Interval(upper * value, lower * value, rightClosed, leftClosed);
1373 }
1374 else {
1375 if (leftClosed && rightClosed) {
1376 return Interval(0, 0, true, true);
1377 }
1378 else {
1379 return EmptyInterval;
1380 }
1381 }
1382}
1384 if (isEmpty()) {
1385 return EmptyInterval;
1386 }
1387 if (value == 0) {
1388 throw std::domain_error("Division by zero");
1389 }
1390 if (value > 0) {
1391 return Interval(lower / value, upper / value, leftClosed, rightClosed);
1392 }
1393 else {
1394 return Interval(upper / value, lower / value, rightClosed, leftClosed);
1395 }
1396}
1397
1399 if (isEmpty() || other.isEmpty()) {
1400 return EmptyInterval;
1401 }
1402 return Interval(lower + other.lower, upper + other.upper, leftClosed && other.leftClosed, rightClosed && other.rightClosed);
1403}
1404
1406 if (isEmpty() || other.isEmpty()) {
1407 return EmptyInterval;
1408 }
1409 return Interval(lower - other.upper, upper - other.lower, leftClosed && other.rightClosed, rightClosed && other.leftClosed);
1410}
1411
1413 if (isEmpty() || other.isEmpty()) {
1414 return EmptyInterval;
1415 }
1416 Number p1 = lower * other.lower;
1417 Number p2 = lower * other.upper;
1418 Number p3 = upper * other.lower;
1419 Number p4 = upper * other.upper;
1420
1421 Number newLower = std::min({p1, p2, p3, p4});
1422 Number newUpper = std::max({p1, p2, p3, p4});
1423 bool newLeftClosed = false;
1424 bool newRightClosed = false;
1425 if (p1 == newLower) {
1426 newLeftClosed = leftClosed && other.leftClosed;
1427 }
1428 else if (p2 == newLower) {
1429 newLeftClosed = leftClosed && other.rightClosed;
1430 }
1431 else if (p3 == newLower) {
1432 newLeftClosed = rightClosed && other.leftClosed;
1433 }
1434 else if (p4 == newLower) {
1435 newLeftClosed = rightClosed && other.rightClosed;
1436 }
1437
1438 if (p1 == newUpper) {
1439 newRightClosed = leftClosed && other.leftClosed;
1440 }
1441 else if (p2 == newUpper) {
1442 newRightClosed = leftClosed && other.rightClosed;
1443 }
1444 else if (p3 == newUpper) {
1445 newRightClosed = rightClosed && other.leftClosed;
1446 }
1447 else if (p4 == newUpper) {
1448 newRightClosed = rightClosed && other.rightClosed;
1449 }
1450
1451 return Interval(newLower, newUpper, newLeftClosed, newRightClosed);
1452}
1453
1455 return this->divReal(other);
1456}
1457
1458Interval Interval::add(const Number &value) const { return *this + value; }
1459
1460Interval Interval::add(const Interval &other) const { return *this + other; }
1461
1462Interval Interval::sub(const Number &value) const { return *this - value; }
1463
1464Interval Interval::sub(const Interval &other) const { return *this - other; }
1465
1466Interval Interval::mul(const Number &value) const { return *this * value; }
1467
1468Interval Interval::mul(const Interval &other) const { return *this * other; }
1469
1471
1473
1474bool Interval::isLeftClosed() const { return leftClosed; }
1475
1476bool Interval::isRightClosed() const { return rightClosed; }
1477
1478Interval Interval::divReal(const Number &value) const {
1479 if (isEmpty()) {
1480 return EmptyInterval;
1481 }
1482 if (value == 0) {
1483 throw std::domain_error("Division by zero");
1484 }
1485 if (value > 0) {
1486 return Interval(lower / value, upper / value, leftClosed, rightClosed);
1487 }
1488 else {
1489 return Interval(upper / value, lower / value, rightClosed, leftClosed);
1490 }
1491}
1492
1494 if (isEmpty() || other.isEmpty()) {
1495 return EmptyInterval;
1496 }
1497 // if the divisor interval contains 0, the result is undefined
1498 if ((other.lower < 0 && other.upper > 0) ||
1499 (other.lower == 0 && other.leftClosed) ||
1500 (other.upper == 0 && other.rightClosed)) {
1501 return FullInterval;
1502 }
1503
1504 // if the dividend interval is zero, the result is zero
1505 if (lower == 0 && upper == 0 && leftClosed && rightClosed) {
1506 return Interval(0, 0, true, true);
1507 }
1508
1509 condAssert(!other.contains(0), "Division by interval containing zero");
1510
1511 Number p1 = lower / other.lower;
1512 Number p2 = lower / other.upper;
1513 Number p3 = upper / other.lower;
1514 Number p4 = upper / other.upper;
1515 Number newLower = std::min({p1, p2, p3, p4});
1516 Number newUpper = std::max({p1, p2, p3, p4});
1517 bool newLeftClosed = false;
1518 bool newRightClosed = false;
1519 if (p1 == newLower) {
1520 newLeftClosed = leftClosed && other.leftClosed;
1521 }
1522 else if (p2 == newLower) {
1523 newLeftClosed = leftClosed && other.rightClosed;
1524 }
1525 else if (p3 == newLower) {
1526 newLeftClosed = rightClosed && other.leftClosed;
1527 }
1528 else if (p4 == newLower) {
1529 newLeftClosed = rightClosed && other.rightClosed;
1530 }
1531
1532 if (p1 == newUpper) {
1533 newRightClosed = leftClosed && other.leftClosed;
1534 }
1535 else if (p2 == newUpper) {
1536 newRightClosed = leftClosed && other.rightClosed;
1537 }
1538 else if (p3 == newUpper) {
1539 newRightClosed = rightClosed && other.leftClosed;
1540 }
1541 else if (p4 == newUpper) {
1542 newRightClosed = rightClosed && other.rightClosed;
1543 }
1544
1545 return Interval(newLower, newUpper, newLeftClosed, newRightClosed);
1546}
1547
1548Interval Interval::divInt(const Number &value) const {
1549 if (isEmpty()) {
1550 return EmptyInterval;
1551 }
1552 if (value == 0) {
1553 throw std::domain_error("Division by zero");
1554 }
1555 // divInt
1556 if (value > 0) {
1557 return Interval((lower / value).floor(), (upper / value).floor(), leftClosed, rightClosed);
1558 }
1559 else {
1560 return Interval((upper / value).floor(), (lower / value).floor(), rightClosed, leftClosed);
1561 }
1562}
1563
1565 if (isEmpty() || other.isEmpty()) {
1566 return EmptyInterval;
1567 }
1568 // if the divisor interval contains 0, the result is undefined
1569 if ((other.lower < 0 && other.upper > 0) ||
1570 (other.lower == 0 && other.leftClosed) ||
1571 (other.upper == 0 && other.rightClosed)) {
1572 return FullInterval;
1573 }
1574
1575 // if the dividend interval is zero, the result is zero
1576 if (lower == 0 && upper == 0 && leftClosed && rightClosed) {
1577 return Interval(0, 0, true, true);
1578 }
1579
1580 Number p1 = (lower / other.lower).floor();
1581 Number p2 = (lower / other.upper).floor();
1582 Number p3 = (upper / other.lower).floor();
1583 Number p4 = (upper / other.upper).floor();
1584 Number newLower = std::min({p1, p2, p3, p4});
1585 Number newUpper = std::max({p1, p2, p3, p4});
1586 bool newLeftClosed = false;
1587 bool newRightClosed = false;
1588 if (p1 == newLower) {
1589 newLeftClosed = leftClosed && other.leftClosed;
1590 }
1591 else if (p2 == newLower) {
1592 newLeftClosed = leftClosed && other.rightClosed;
1593 }
1594 else if (p3 == newLower) {
1595 newLeftClosed = rightClosed && other.leftClosed;
1596 }
1597 else if (p4 == newLower) {
1598 newLeftClosed = rightClosed && other.rightClosed;
1599 }
1600
1601 if (p1 == newUpper) {
1602 newRightClosed = leftClosed && other.leftClosed;
1603 }
1604 else if (p2 == newUpper) {
1605 newRightClosed = leftClosed && other.rightClosed;
1606 }
1607 else if (p3 == newUpper) {
1608 newRightClosed = rightClosed && other.leftClosed;
1609 }
1610 else if (p4 == newUpper) {
1611 newRightClosed = rightClosed && other.rightClosed;
1612 }
1613
1614 return Interval(newLower, newUpper, newLeftClosed, newRightClosed);
1615}
1616
1617Interval Interval::mod(const Number &value) const {
1618 if (isEmpty()) {
1619 return EmptyInterval;
1620 }
1621 if (value == Number(0)) {
1622 throw std::domain_error("Modulo by zero");
1623 }
1624 else if (value < Number(0)) {
1625 throw std::domain_error("Modulo by negative value");
1626 }
1627
1628 // For modulo operation, the result is usually in the range [0, |value|)
1629 Number absValue = value.abs();
1630 Number newLower = Number(0);
1631 Number newUpper = absValue - Number(1);
1632
1633 // Handle boundary conditions based on the original interval
1634 bool newLeftClosed = true; // Lower bound is always 0 with closed interval
1635 bool newRightClosed = true;
1636
1637 // Adjust upper bound if necessary
1638 if (newUpper > upper) {
1639 newUpper = upper;
1640 newRightClosed = rightClosed;
1641 }
1642 else if (newUpper < lower) {
1643 // If modulus is smaller than lower bound of original interval,
1644 // take closedness from the lower bound
1645 newRightClosed = leftClosed;
1646 }
1647
1648 return Interval(newLower, newUpper, newLeftClosed, newRightClosed);
1649}
1650
1651Interval Interval::mod(const Interval &other) const {
1652 if (isEmpty() || other.isEmpty()) {
1653 return EmptyInterval;
1654 }
1655
1656 // Check for zero in divisor interval
1657 if ((other.lower < Number(0) && other.upper > Number(0)) ||
1658 (other.lower == Number(0) && other.leftClosed) ||
1659 (other.upper == Number(0) && other.rightClosed)) {
1660 throw std::domain_error("Modulo by interval containing zero");
1661 }
1662
1663 // For negative divisors, convert to positive (|a| mod |b| = a mod b for b >
1664 // 0)
1665 Number absLower = other.lower.abs();
1666 Number absUpper = other.upper.abs();
1667
1668 // The result of modulo is always in [0, max(|divisor|))
1669 Number maxDivisor = std::max(absLower, absUpper);
1670
1671 // Conservatively, the result can be anywhere in [0, maxDivisor-1]
1672 // A more precise implementation would consider the specific values
1673 // in both intervals, but this is a safe approximation
1674 Number resultLower = Number(0);
1675 Number resultUpper = maxDivisor - Number(1);
1676
1677 // Refine the upper bound if the original interval is smaller
1678 if (upper < resultUpper) {
1679 resultUpper = upper;
1680 return Interval(resultLower, resultUpper, true, rightClosed);
1681 }
1682
1683 return Interval(resultLower, resultUpper, true, true);
1684}
1685
1687 return this->mod(other);
1688}
1689
1691 return this->mod(value);
1692}
1693
1695 return this->pow(other);
1696}
1697
1699 return this->pow(value);
1700}
1701
1703 if (isEmpty()) {
1704 return EmptyInterval;
1705 }
1706 // power of 2: 2^x
1707 Number newLower = Number(0);
1708 Number newUpper = Number::positiveInfinity();
1709 bool newLeftClosed = this->isLeftClosed();
1710 bool newRightClosed = this->isRightClosed();
1711 if (lower.isPositiveInfinity()) {
1712 newLower = Number::positiveInfinity();
1713 newLeftClosed = false;
1714 }
1715 else if (lower.isNegativeInfinity()) {
1716 // 2^(-inf) = 0
1717 newLower = Number(0);
1718 newLeftClosed = false;
1719 }
1720 else {
1721 if (lower.isInteger()) {
1722 newLower = Number(2).pow(lower.toInteger());
1723 }
1724 else {
1725 newLower = Number(2).pow(lower);
1726 newLower = newLower.nextBelow();
1727 newLeftClosed = false;
1728 }
1729 }
1730
1731 if (upper.isPositiveInfinity()) {
1732 newUpper = Number::positiveInfinity();
1733 newRightClosed = false;
1734 }
1735 else if (upper.isNegativeInfinity()) {
1736 newUpper = Number(0);
1737 newRightClosed = false;
1738 }
1739 else {
1740 if (upper.isInteger()) {
1741 newUpper = Number(2).pow(upper.toInteger());
1742 }
1743 else {
1744 newUpper = Number(2).pow(upper);
1745 newUpper = newUpper.nextAbove();
1746 newRightClosed = false;
1747 }
1748 }
1749
1750 condAssert(newLower >= Number(0), "2^x is always positive");
1751
1752 return Interval(newLower, newUpper, newLeftClosed, newRightClosed);
1753}
1754
1755Interval Interval::pow(const Number &exp) const {
1756 if (isEmpty()) {
1757 return EmptyInterval;
1758 }
1759
1760 if (exp.isInfinity()) {
1761 if (exp.isPositiveInfinity()) {
1762 // x^inf
1763 if (lower > Number(1)) {
1765 }
1766 else if (lower >= Number(0) && upper <= Number(1)) {
1767 if (lower == Number(0) && upper == Number(1)) {
1768 return Interval(Number(0), Number(1), true, true);
1769 }
1770 else {
1771 return Interval(Number(0), Number(0).nextAbove(), false, false);
1772 }
1773 }
1774 else if (lower < Number(0)) {
1775 throw std::domain_error(
1776 "Cannot compute infinite power with negative base");
1777 }
1778 }
1779 else {
1780 // x^-inf
1781 if (lower > Number(1)) {
1782 return Interval(Number(0), Number(0).nextAbove(), false, false);
1783 }
1784 else if (lower >= Number(0) && upper <= Number(1)) {
1785 if (upper == Number(1)) {
1786 return Interval(Number(1), Number::positiveInfinity(), true, false);
1787 }
1788 else {
1791 false,
1792 false);
1793 }
1794 }
1795 else if (lower <= Number(0) && upper >= Number(0)) {
1796 throw std::domain_error(
1797 "Cannot compute infinite power of interval containing zero");
1798 }
1799 else {
1800 throw std::domain_error(
1801 "Cannot compute infinite power with complex results");
1802 }
1803 }
1804 }
1805
1806 // If exp is an integer
1807 if (exp.isInteger()) {
1808 int n = exp.toInteger().toInt();
1809
1810 if (n == 0) {
1811 // x^0 = 1 (assuming x != 0)
1812 return Interval(Number(1), Number(1), true, true);
1813 }
1814 else if (n > 0) {
1815 if (n % 2 == 0) {
1816 // even power
1817 if (lower >= Number(0)) {
1818 // [0, inf) ^ even = [0, inf)
1819 return Interval(lower.pow(n), upper.pow(n), leftClosed, rightClosed);
1820 }
1821 else if (upper <= Number(0)) {
1822 // (-inf, 0] ^ even = [0, inf)
1823 // flip the closedness, because the even power of negative number will
1824 // reverse the interval direction
1825 return Interval(upper.pow(n), lower.pow(n), rightClosed, leftClosed);
1826 }
1827 else {
1828 // the interval crosses zero, the minimum value is at x=0
1829 Number maxVal = std::max(lower.abs().pow(n), upper.abs().pow(n));
1830 return Interval(
1831 Number(0), maxVal, true, ((lower.abs() > upper.abs()) ? leftClosed : rightClosed));
1832 }
1833 }
1834 else {
1835 // odd power - monotonically increasing
1836 return Interval(lower.pow(n), upper.pow(n), leftClosed, rightClosed);
1837 }
1838 }
1839 else { // n < 0
1840 // negative power - need to handle the case of division by zero
1841 if (lower <= Number(0) && upper >= Number(0)) {
1842 // the interval contains zero, the result is undefined
1843 throw std::domain_error(
1844 "Cannot compute negative power of interval containing zero");
1845 }
1846 else {
1847 // apply x^(-n) = 1/(x^n)
1848 if (n % 2 == 0) {
1849 // even negative power
1850 if (lower > Number(0) || upper < Number(0)) {
1851 // positive or negative base
1852 // for even negative power, the result is always positive
1853 // when the base is positive, the function is monotonically
1854 // decreasing
1855 if (lower > Number(0)) {
1856 return Interval(upper.pow(n), lower.pow(n), rightClosed, leftClosed);
1857 }
1858 // when the base is negative, the function is monotonically
1859 // decreasing
1860 else {
1861 // for negative number, compare the absolute value
1862 return Interval(lower.abs().pow(n), upper.abs().pow(n), leftClosed, rightClosed);
1863 }
1864 }
1865 }
1866 else {
1867 // odd negative power
1868 // for odd negative power, the function is monotonically decreasing
1869 // and keep the sign of the original number
1870 return Interval(upper.pow(n), lower.pow(n), rightClosed, leftClosed);
1871 }
1872 }
1873 }
1874 }
1875
1876 // non-integer power
1877 // only when the base is positive has meaning
1878 if (lower > Number(0)) {
1879 if (lower.isInfinity() || upper.isInfinity()) {
1880 if (exp > Number(0)) {
1882 }
1883 else {
1884 return Interval(Number(0), Number(0).nextAbove(), false, false);
1885 }
1886 }
1887
1888 // non-integer power produces a floating error, so we need to use the
1889 // nextBelow and nextAbove to get the result
1891 }
1892 else if (lower <= Number(0) && exp >= Number(0)) {
1893 // if the base contains 0 or negative number, and the exponent is
1894 // non-negative, then special processing is needed
1895 if (upper <= Number(0)) {
1896 // if the whole interval is negative, and the exponent is not an integer,
1897 // the result is undefined
1898 throw std::domain_error(
1899 "Cannot compute non-integer power of negative interval");
1900 }
1901 // the interval crosses zero, the result starts from 0
1902 return Interval(Number(0), upper.pow(exp).nextAbove(), true, false);
1903 }
1904 else {
1905 // for negative number, the non-integer power is undefined
1906 throw std::domain_error(
1907 "Cannot compute non-integer power with negative base");
1908 }
1909}
1910
1912 if (isEmpty() || exp.isEmpty()) {
1913 return EmptyInterval;
1914 }
1915
1916 if (exp.isPoint()) {
1917 return pow(exp.getLower());
1918 }
1919
1920 if (isPoint()) {
1921 auto val = getLower();
1922 if (val == Number(0) || val == Number(1)) {
1923 return Interval(val, val, true, true);
1924 }
1925 else if (val == Number(2)) {
1926 return exp.pow2();
1927 }
1928 else {
1929 // power of val: val^x
1930 Number newLower = Number(0);
1931 Number newUpper = Number::positiveInfinity();
1932 bool newLeftClosed = true;
1933 bool newRightClosed = true;
1935 newLower = Number::positiveInfinity();
1936 newLeftClosed = false;
1937 }
1938 else if (exp.getLower().isNegativeInfinity()) {
1939 // val^(-inf) = 0
1940 newLower = Number(0);
1941 newLeftClosed = false;
1942 }
1943 else {
1944 if (exp.getLower().isInteger() && val.isInteger()) {
1945 newLower = val.pow(exp.getLower().toInteger());
1946 }
1947 else {
1948 newLower = val.pow(exp.getLower());
1949 newLower = newLower.nextBelow();
1950 newLeftClosed = false;
1951 }
1952 }
1953
1955 newUpper = Number::positiveInfinity();
1956 newRightClosed = false;
1957 }
1958 else if (exp.getUpper().isNegativeInfinity()) {
1959 newUpper = Number(0);
1960 newRightClosed = false;
1961 }
1962 else {
1963 if (exp.getUpper().isInteger() && val.isInteger()) {
1964 newUpper = val.pow(exp.getUpper().toInteger());
1965 }
1966 else {
1967 newUpper = val.pow(exp.getUpper());
1968 newUpper = newUpper.nextAbove();
1969 newRightClosed = false;
1970 }
1971 }
1972
1973 return Interval(newLower, newUpper, newLeftClosed, newRightClosed);
1974 }
1975 }
1976
1977 // if the base interval is completely positive
1978 if (lower > Number(0)) {
1979 // calculate the four corner points
1980 std::vector<Number> vals;
1981 vals.push_back(lower.pow(exp.getLower()));
1982 vals.push_back(lower.pow(exp.getUpper()));
1983 vals.push_back(upper.pow(exp.getLower()));
1984 vals.push_back(upper.pow(exp.getUpper()));
1985 vals.erase(std::remove_if(vals.begin(), vals.end(), [](Number x) { return x.isNaN(); }),
1986 vals.end());
1987 // if all the values are NaN, then the result is undefined
1988 if (vals.empty()) {
1989 return FullInterval;
1990 }
1991
1992 // find the minimum and maximum values
1993 Number minVal = vals[0];
1994 Number maxVal = vals[0];
1995 for (auto val : vals) {
1996 if (val < minVal) {
1997 minVal = val;
1998 }
1999 if (val > maxVal) {
2000 maxVal = val;
2001 }
2002 }
2003
2004 // the closedness depends on the relation between the corner points and the
2005 // boundary values
2006 bool newLeftClosed = false, newRightClosed = false;
2007
2008 if (minVal == vals[0]) {
2009 newLeftClosed = leftClosed && exp.isLeftClosed();
2010 }
2011 else if (minVal == vals[1]) {
2012 newLeftClosed = leftClosed && exp.isRightClosed();
2013 }
2014 else if (minVal == vals[2]) {
2015 newLeftClosed = rightClosed && exp.isLeftClosed();
2016 }
2017 else if (minVal == vals[3]) {
2018 newLeftClosed = rightClosed && exp.isRightClosed();
2019 }
2020
2021 if (maxVal == vals[0]) {
2022 newRightClosed = leftClosed && exp.isLeftClosed();
2023 }
2024 else if (maxVal == vals[1]) {
2025 newRightClosed = leftClosed && exp.isRightClosed();
2026 }
2027 else if (maxVal == vals[2]) {
2028 newRightClosed = rightClosed && exp.isLeftClosed();
2029 }
2030 else if (maxVal == vals[3]) {
2031 newRightClosed = rightClosed && exp.isRightClosed();
2032 }
2033 return Interval(minVal, maxVal, newLeftClosed, newRightClosed);
2034 }
2035
2036 // if the base contains 0 or negative number, and the exponent is not a fixed
2037 // integer, then special processing is needed this case is very complex, and
2038 // may need to discuss different intervals
2039 throw std::domain_error(
2040 "Cannot compute interval power with negative or zero "
2041 "base and non-integer exponent");
2042}
2043
2045 if (isEmpty() || x.isNaN()) {
2046 return EmptyInterval;
2047 }
2048
2049 // the domain of atan2(y, x) is the whole plane, and it will not cause
2050 // division by zero problem the range of atan2 is [-π, π]
2051
2052 // if x is a fixed number, then the result is the atan2 of each point in the
2053 // interval
2054 Number low = Number::atan2(lower, x);
2055 Number high = Number::atan2(upper, x);
2056
2057 // special case: if the interval crosses the x-axis, and x < 0, then the
2058 // result will cross the π or -π discontinuity point
2059 if (lower < Number(0) && upper > Number(0) && x < Number(0)) {
2060 // in this case, the result of atan2 is the complete range of [-π, π]
2061 return Interval(-Number::pi(), Number::pi(), true, true);
2062 }
2063 bool newLeftClosed = leftClosed;
2064 bool newRightClosed = rightClosed;
2065 // check if the result needs to be sorted in ascending order
2066 if (low > high) {
2067 std::swap(low, high);
2068 std::swap(newLeftClosed, newRightClosed);
2069 }
2070
2071 // add a small value
2072 if (low < -Number::pi()) {
2073 low = -Number::pi();
2074 newLeftClosed = true;
2075 }
2076 else {
2077 low = low.nextBelow();
2078 newLeftClosed = false;
2079 }
2080
2081 if (high > Number::pi()) {
2082 high = Number::pi();
2083 newRightClosed = true;
2084 }
2085 else {
2086 high = high.nextAbove();
2087 newRightClosed = false;
2088 }
2089 return Interval(low, high, newLeftClosed, newRightClosed);
2090}
2091
2093 if (isEmpty() || x.isEmpty()) {
2094 return EmptyInterval;
2095 }
2096
2097 // the domain of atan2(y, x) is the whole plane, and the range is [-π, π]
2098
2099 // we need to check if there are some special cases:
2100
2101 // 1. if y and x interval both contain 0, then the result can be any angle
2102 if ((lower <= Number(0) && upper >= Number(0)) &&
2103 (x.getLower() <= Number(0) && x.getUpper() >= Number(0))) {
2104 return Interval(-Number::pi(), Number::pi(), true, true);
2105 }
2106
2107 // 2. if y interval crosses 0, and x is completely positive or negative
2108 if (lower < Number(0) && upper > Number(0)) {
2109 if (x.getUpper() < Number(0)) {
2110 // x is completely negative, the result is [-π, 0] U [0, π], which is the
2111 // whole [-π, π]
2112 return Interval(-Number::pi(), Number::pi(), true, true);
2113 }
2114 else if (x.getLower() > Number(0)) {
2115 // x is completely positive, the result is [-π/2, π/2]
2116 return Interval(-Number::pi() / Number(2), Number::pi() / Number(2), true, true);
2117 }
2118 }
2119
2120 // 3. if x interval crosses 0, and y is completely positive or negative
2121 if (x.getLower() < Number(0) && x.getUpper() > Number(0)) {
2122 if (lower > Number(0)) {
2123 // y is completely positive, the result is [0, π]
2124 return Interval(Number(0), Number::pi(), true, true);
2125 }
2126 else if (upper < Number(0)) {
2127 // y is completely negative, the result is [-Ï€, 0]
2128 return Interval(-Number::pi(), Number(0), true, true);
2129 }
2130 }
2131
2132 // for other cases, we need to calculate the four corner points
2133 Number vals[4] = {
2135
2136 // find the minimum and maximum values
2137 Number minVal = std::min({vals[0], vals[1], vals[2], vals[3]});
2138 Number maxVal = std::max({vals[0], vals[1], vals[2], vals[3]});
2139
2140 // check if it crosses the discontinuity point π or -π
2141 if (maxVal - minVal > Number::pi()) {
2142 // if the maximum value and minimum value differ by more than π, it means it
2143 // crosses the discontinuity point
2144 return Interval(-Number::pi(), Number::pi(), true, true);
2145 }
2146
2147 // the closedness depends on the relation between the corner points and the
2148 // boundary values
2149 bool newLeftClosed = false, newRightClosed = false;
2150
2151 if (minVal == vals[0])
2152 newLeftClosed = leftClosed && x.isLeftClosed();
2153 else if (minVal == vals[1])
2154 newLeftClosed = leftClosed && x.isRightClosed();
2155 else if (minVal == vals[2])
2156 newLeftClosed = rightClosed && x.isLeftClosed();
2157 else if (minVal == vals[3])
2158 newLeftClosed = rightClosed && x.isRightClosed();
2159
2160 if (maxVal == vals[0])
2161 newRightClosed = leftClosed && x.isLeftClosed();
2162 else if (maxVal == vals[1])
2163 newRightClosed = leftClosed && x.isRightClosed();
2164 else if (maxVal == vals[2])
2165 newRightClosed = rightClosed && x.isLeftClosed();
2166 else if (maxVal == vals[3])
2167 newRightClosed = rightClosed && x.isRightClosed();
2168
2169 // add a small value
2170 if (minVal < -Number::pi()) {
2171 minVal = -Number::pi();
2172 newLeftClosed = true;
2173 }
2174 else {
2175 minVal = minVal.nextBelow();
2176 newLeftClosed = false;
2177 }
2178
2179 if (maxVal > Number::pi()) {
2180 maxVal = Number::pi();
2181 newRightClosed = true;
2182 }
2183 else {
2184 maxVal = maxVal.nextAbove();
2185 newRightClosed = false;
2186 }
2187
2188 return Interval(minVal, maxVal, newLeftClosed, newRightClosed);
2189}
2190
2191bool Interval::operator<(const Interval &other) const {
2192 if (isEmpty() || other.isEmpty()) {
2193 throw std::invalid_argument("Cannot compare empty intervals");
2194 }
2195
2196 // An interval A is strictly less than interval B if:
2197 // The upper bound of A is less than the lower bound of B
2198 // Or they touch at a single point but at least one interval is open at that
2199 // point
2200 return upper < other.lower ||
2201 (upper == other.lower && (!rightClosed || !other.leftClosed));
2202}
2203
2204bool Interval::operator<=(const Interval &other) const {
2205 if (isEmpty() || other.isEmpty()) {
2206 throw std::invalid_argument("Cannot compare empty intervals");
2207 }
2208
2209 // A <= B means that it's not true that A > B
2210 return !(*this > other);
2211}
2212
2213bool Interval::operator>(const Interval &other) const {
2214 if (isEmpty() || other.isEmpty()) {
2215 throw std::invalid_argument("Cannot compare empty intervals");
2216 }
2217
2218 // An interval A is strictly greater than interval B if:
2219 // The lower bound of A is greater than the upper bound of B
2220 // Or they touch at a single point but at least one interval is open at that
2221 // point
2222 return lower > other.upper ||
2223 (lower == other.upper && (!leftClosed || !other.rightClosed));
2224}
2225
2226bool Interval::operator>=(const Interval &other) const {
2227 if (isEmpty() || other.isEmpty()) {
2228 throw std::invalid_argument("Cannot compare empty intervals");
2229 }
2230
2231 // A >= B means that it's not true that A < B
2232 return !(*this < other);
2233}
2234
2236 lower += value;
2237 upper += value;
2238 return *this;
2239}
2240
2242 lower -= value;
2243 upper -= value;
2244 return *this;
2245}
2246
2248 if (value >= 0) {
2249 lower *= value;
2250 upper *= value;
2251 }
2252 else {
2253 Number temp = lower;
2254 lower = upper * value;
2255 upper = temp * value;
2256 bool tempClosed = leftClosed;
2258 rightClosed = tempClosed;
2259 }
2260 return *this;
2261}
2262
2264 if (value == 0) {
2265 throw std::domain_error("Division by zero");
2266 }
2267 if (value > 0) {
2268 lower /= value;
2269 upper /= value;
2270 }
2271 else {
2272 Number temp = lower;
2273 lower = upper / value;
2274 upper = temp / value;
2275 bool tempClosed = leftClosed;
2277 rightClosed = tempClosed;
2278 }
2279 return *this;
2280}
2281
2283 if (isEmpty()) {
2284 return EmptyInterval;
2285 }
2286
2287 switch (kind) {
2288 case NODE_KIND::NT_NEG:
2289 return this->negate();
2290 case NODE_KIND::NT_ABS:
2291 return this->abs();
2292 case NODE_KIND::NT_LB:
2293 return this->lb();
2294 case NODE_KIND::NT_LN:
2295 return this->ln();
2296 case NODE_KIND::NT_LG:
2297 return this->lg();
2298 case NODE_KIND::NT_EXP:
2299 return this->exp();
2300 case NODE_KIND::NT_SQRT:
2301 return this->sqrt();
2303 return this->safeSqrt();
2304 case NODE_KIND::NT_SIN:
2305 return this->sin();
2306 case NODE_KIND::NT_COS:
2307 return this->cos();
2308 case NODE_KIND::NT_TAN:
2309 return this->tan();
2310 case NODE_KIND::NT_COT:
2311 return this->cot();
2312 case NODE_KIND::NT_SEC:
2313 return this->sec();
2314 case NODE_KIND::NT_CSC:
2315 return this->csc();
2316 case NODE_KIND::NT_ASIN:
2317 return this->asin();
2318 case NODE_KIND::NT_ACOS:
2319 return this->acos();
2320 case NODE_KIND::NT_ATAN:
2321 return this->atan();
2322 case NODE_KIND::NT_ACOT:
2323 return this->acot();
2324 case NODE_KIND::NT_ASEC:
2325 return this->asec();
2326 case NODE_KIND::NT_ACSC:
2327 return this->acsc();
2328 case NODE_KIND::NT_SINH:
2329 return this->sinh();
2330 case NODE_KIND::NT_COSH:
2331 return this->cosh();
2332 case NODE_KIND::NT_TANH:
2333 return this->tanh();
2334 case NODE_KIND::NT_COTH:
2335 return this->coth();
2336 case NODE_KIND::NT_SECH:
2337 return this->sech();
2338 case NODE_KIND::NT_CSCH:
2339 return this->csch();
2341 return this->asinh();
2343 return this->acosh();
2345 return this->atanh();
2347 return this->acoth();
2349 return this->asech();
2351 return this->acsch();
2352 default:
2353 throw std::invalid_argument("Unsupported unary operation");
2354 }
2355}
2356
2357Interval Interval::operate(const NODE_KIND &kind, const Number &value) const {
2358 if (isEmpty()) {
2359 return EmptyInterval;
2360 }
2361
2362 switch (kind) {
2363 case NODE_KIND::NT_ADD:
2364 return (*this + value);
2365 case NODE_KIND::NT_SUB:
2366 return (*this - value);
2367 case NODE_KIND::NT_MUL:
2368 return (*this * value);
2370 return this->divReal(value);
2372 return this->divInt(value);
2373 case NODE_KIND::NT_MOD:
2374 return this->mod(value);
2375 case NODE_KIND::NT_POW:
2376 return this->pow(value);
2378 return this->atan2(value);
2379 case NODE_KIND::NT_LT:
2380 case NODE_KIND::NT_LE:
2381 case NODE_KIND::NT_GT:
2382 case NODE_KIND::NT_GE:
2383 case NODE_KIND::NT_EQ:
2385 // comparison operations return boolean values, not supported for interval
2386 // arithmetic
2387 throw std::invalid_argument(
2388 "Comparison operations not supported for interval arithmetic");
2389 default:
2390 throw std::invalid_argument("Unsupported binary operation with Number");
2391 }
2392}
2393
2394Interval Interval::operate(const NODE_KIND &kind, const Interval &other) const {
2395 if (isEmpty() || other.isEmpty()) {
2396 throw std::invalid_argument("One or both intervals are empty");
2397 }
2398
2399 switch (kind) {
2400 case NODE_KIND::NT_ADD:
2401 return (*this + other);
2402 case NODE_KIND::NT_SUB:
2403 return (*this - other);
2404 case NODE_KIND::NT_MUL:
2405 return (*this * other);
2407 return this->divReal(other);
2409 return this->divInt(other);
2410 case NODE_KIND::NT_MOD:
2411 return this->mod(other);
2412 case NODE_KIND::NT_POW:
2413 return this->pow(other);
2415 return this->atan2(other);
2416 case NODE_KIND::NT_AND:
2417 // and operation
2418 if (this->isIntersectingWith(other)) {
2419 return this->intersection(other);
2420 }
2421 else {
2422 // return empty interval
2423 return Interval(Number(1), Number(0), false, false);
2424 }
2425 case NODE_KIND::NT_OR:
2426 // or operation
2427 if (this->isIntersectingWith(other) || !this->isDisjointFrom(other)) {
2428 return this->unionWith(other);
2429 }
2430 else {
2431 // non-intersecting intervals cannot be represented by a single interval,
2432 // return the smallest interval containing both
2433 Number newLower = std::min(lower, other.lower);
2434 Number newUpper = std::max(upper, other.upper);
2435 return Interval(newLower, newUpper, false, false);
2436 }
2437 case NODE_KIND::NT_LT:
2438 case NODE_KIND::NT_LE:
2439 case NODE_KIND::NT_GT:
2440 case NODE_KIND::NT_GE:
2441 case NODE_KIND::NT_EQ:
2443 // comparison operations return boolean values, not supported for interval
2444 // arithmetic
2445 throw std::invalid_argument(
2446 "Comparison operations not supported for interval arithmetic");
2447 default:
2448 throw std::invalid_argument("Unsupported binary operation with Interval");
2449 }
2450}
2451} // namespace stabilizer::parser
#define condAssert(cond, msg)
Definition asserting.h:35
void getIntegers(std::vector< Number > &integers) const
Definition interval.cpp:106
Interval operator~() const
Definition interval.cpp:396
Interval add(const Number &value) const
Interval operator--() const
Definition interval.cpp:378
bool isIntersectingWith(const Interval &other) const
Definition interval.cpp:185
bool isSupersetOf(const Interval &other) const
Definition interval.cpp:154
Interval(Number lower=Number::zero(), Number upper=Number::zero(), bool leftClosed=true, bool rightClosed=true)
Definition interval.cpp:35
void setLeftClosed(bool leftClosed)
Definition interval.cpp:76
void setUpper(const Number &upper)
Definition interval.cpp:69
Interval & operator/=(const Number &value)
void setLower(const Number &lower)
Definition interval.cpp:62
Interval divReal(const Number &value) const
Interval operator+() const
Definition interval.cpp:390
Interval mul(const Number &value) const
Interval operator!() const
Definition interval.cpp:402
bool operator==(const Interval &other) const
Definition interval.cpp:53
bool operator<=(const Interval &other) const
Interval intersection(const Interval &other) const
Definition interval.cpp:193
Interval & operator-=(const Number &value)
Interval operator%(const Number &value) const
Interval operate(const NODE_KIND &kind) const
Interval unionWith(const Interval &other) const
Definition interval.cpp:209
bool operator<(const Interval &other) const
Interval mod(const Number &value) const
static std::vector< Interval > unionMulti(const std::vector< Interval > &intervals)
Definition interval.cpp:324
Interval operator++() const
Definition interval.cpp:372
std::vector< Interval > difference(const Interval &other) const
Definition interval.cpp:269
std::string toString() const
Definition interval.cpp:88
bool operator!=(const Interval &other) const
Definition interval.cpp:58
bool isSubsetEqOf(const Interval &other) const
Definition interval.cpp:234
bool isDisjointFrom(const Interval &other) const
Definition interval.cpp:170
size_t getIntervalIntCount() const
Definition interval.cpp:244
bool contains(const Number &value) const
Definition interval.cpp:129
Interval operator/(const Number &value) const
Interval pow(const Number &exp) const
Interval divInt(const Number &value) const
Interval & operator*=(const Number &value)
Interval & operator=(const Interval &other)
Definition interval.cpp:43
bool operator>=(const Interval &other) const
Interval operator-() const
Definition interval.cpp:384
void setRightClosed(bool rightClosed)
Definition interval.cpp:78
Interval & operator+=(const Number &value)
Interval atan2(const Number &x) const
bool operator>(const Interval &other) const
Interval safeSqrt() const
Definition interval.cpp:654
Interval operator*(const Number &value) const
Interval sub(const Number &value) const
Interval operator^(const Number &value) const
bool isSubsetOf(const Interval &other) const
Definition interval.cpp:137
Number pow(const Number &exp) const
Definition number.cpp:1468
std::string toString() const
Definition number.cpp:1431
HighPrecisionInteger toInteger() const
Definition number.cpp:1169
static Number atan2(const Number &y, const Number &x)
Definition number.cpp:1636
bool isPositiveInfinity() const
Definition number.cpp:1229
static Number pi(size_t precision=128)
Definition number.cpp:1235
bool isNegativeInfinity() const
Definition number.cpp:1223
Number nextAbove() const
Definition number.cpp:1734
bool isInteger() const
Definition number.h:312
Number nextBelow() const
Definition number.cpp:1725
static Number positiveInfinity()
Definition number.cpp:1199
static Number negativeInfinity()
Definition number.cpp:1194
Number safeSqrt() const
Definition number.cpp:1460
Interval EmptyInterval
Definition interval.h:185
Interval FullInterval
Definition interval.h:186