#pragma once #include #include #include namespace cgv { namespace math { /** the interval template represents a close interval of two numbers, i.e. [a,b]. All arithmetic operators are overloaded for the interval template. This allows to compute bounds for an arbitrary expression. For example if the variable x is from the interval [-1,1] then the expression x*x is from the interval [0,1], what can be computed via \codebegin interval x(-1,1); interval e = x*x; std::cout << e << std::endl; \codeend Furthermore, the interval template supports further interval based operations like intersection and extension of an interval. */ template class interval { protected: T lb, ub; public: /**@name interval operations*/ //@{ /// construct empty interval interval() : lb(1), ub(0) {} /** construct interval over all valid values of the value type, where the parameter is only a dummy to distinguish this constructor from the standard constructor. This constructor relies on cgv::type::traits::min and cgv::type::traits::max. Thus if you want to build intervals over non standard types, you need to ensure that these two traits are instantiated for your type when using this constructor. */ interval(bool) : lb(type::traits::min_fct::get_value()), ub(type::traits::max_fct::get_value()) {} /// copy constructor interval(const interval& I) : lb(I.get_lower_bound()), ub(I.get_upper_bound()) {} /// contruct interval from bounds, sort bounds if necessary. To construct empty interval call the standard constructor interval(const T& _lb, const T& _ub) : lb(std::min(_lb,_ub)), ub(std::max(_lb,_ub)) {} /// check if interval is empty bool is_empty() const { return ub < lb; } /// set to empty interval void clear() { ub = 0; lb = 1; } /// check if given value is contained in interval bool contains(const T& v) const { return lb <= v && v <= ub; } /// set the lower bound void set_lower_bound(const T& _lb) { lb = _lb; } /// set the upper bound void set_upper_bound(const T& _ub) { ub = _ub; } /// return the lower bound const T& get_lower_bound() const { return lb; } /// return the upper bound const T& get_upper_bound() const { return ub; } /// return the center value, which is only valid if the interval is not empty T get_center() const { return (lb+ub)/2; } /// return the size of the interval T get_size() const { return is_empty() ? 0 : ub-lb; } /// set interval to intersection with given interval and return reference to this interval interval& intersect(const interval& I) { lb = std::max(lb, I.get_lower_bound()); ub = std::min(ub, I.get_upper_bound()); return *this; } /// return intersection interval interval intersection(const interval& I) const { interval J(*this); return J.intersect(I); } /// extend interval such that it includes the given value interval& extend(const T& v) { if (is_empty()) lb=ub=v; else { lb = std::min(lb,v); ub = std::max(ub,v); } return *this; } /// return extension of interval that it includes the given value interval& extension(const T& v) { interval I(*this); return I.extend(v); } /// extend interval such that it includes the given interval interval& extend(const interval& I) { if (!I.is_empty()) { extend(I.get_lower_bound()); extend(I.get_upper_bound()); } return *this; } /// extend by four values interval& extend(const T& v0, const T& v1, const T& v2, const T& v3) { extend(v0); extend(v1); extend(v2); extend(v3); return *this; } /// return extension of interval that it includes the given interval interval& extension(const interval& J) { interval I(*this); return I.extend(J); } //@} /**@name scalar operators */ //@{ /// scale the interval interval& operator *= (const T& s) { lb *= s; ub *= s; return *this; } /// return scaled the interval interval operator * (const T& s) const { interval I(*this); return I *= s; } /// divide the interval interval& operator /= (const T& s) { return *this *= 1/s; } /// return divided the interval interval operator / (const T& s) const { return *this * (1/s); } /// right shift interval by adding scalar to both bounds interval& operator += (const T& s) { lb += s; ub += s; return *this; } /// return right shifted interval interval operator + (const T& s) const { interval I(*this); return I += s; } /// left shift interval by subtracting scalar from both bounds interval& operator -= (const T& s) { lb -= s; ub -= s; return *this; } /// return left shifted interval interval operator - (const T& s) const { interval I(*this); return I -= s; } //@} /**@name interval operators */ //@{ /// compute the interval of all values of all products of values from the two intervals interval& operator *= (const interval& I) { if (is_empty() || I.is_empty()) *this = interval(); else extend(lb*I.get_lower_bound(), lb*I.get_upper_bound(), ub*I.get_lower_bound(), ub*I.get_upper_bound()); return *this; } /// return the interval of all values of products of values from the two intervals interval operator * (const interval& J) const { interval I(*this); return I *= J; } /** compute the interval of all values of all quotients of values from the two intervals. Notice that if zero is contained in the second interval, the result is the complete interval of all valid values and the same precondition holds as for the constructor with the bool dummy parameter interval(bool). */ interval& operator /= (const interval& I) { if (is_empty() || I.is_empty()) *this = interval(); else { if (I.contains(0)) *this = interval(true); else extend(lb/I.get_lower_bound(), lb/I.get_upper_bound(), ub/I.get_lower_bound(), ub/I.get_upper_bound()); } return *this; } /// return the interval of all values of quotients of values from the two intervals. See also comments on the /= operator(). interval operator / (const interval& J) const { interval I(*this); return I /= J; } /// unary minus operator reflects interval at zero value interval operator - () const { if (is_empty()) return *this; else return interval(-ub,-lb); } /// compute interval of all sums of values from the two intervals interval& operator += (const interval& I) { if (is_empty() || I.is_empty()) *this = interval(); else { extend(lb+I.get_lower_bound(), lb+I.get_upper_bound(), ub+I.get_lower_bound(), ub+I.get_upper_bound()); } return *this; } /// return interval of all sums of values from the two intervals interval operator + (const interval& J) const { interval I(*this); return I += J; } /// compute interval of all differences of values from the two intervals interval& operator -= (const interval& I) { return *this += -I; } /// return interval of all differences of values from the two intervals interval operator - (const interval& J) const { interval I(*this); return I -= J; } //@} /**@name comparison operators*/ //@{ /// check for equality of two intervals bool operator == (const interval& I) const { return is_empty() && I.is_empty() || lb == I.get_lower_bound() && ub == I.get_upper_bound(); } /// check for inequality of two intervals bool operator != (const interval& I) const { return !(*this == I); } /// only returns true if both intervals are not empty and the operator holds for all values in both intervals bool operator < (const interval& I) const { return !is_empty() && !I.is_empty() && ub < I.get_lower_bound(); } /// only returns true if both intervals are not empty and the operator holds for all values in both intervals bool operator > (const interval& I) const { return !is_empty() && !I.is_empty() && lb > I.get_upper_bound(); } //@} }; template inline interval operator + (const T& s, const interval& I) { return I+s; } template inline interval operator - (const T& s, const interval& I) { return -I+s; } template inline interval operator * (const T& s, const interval& I) { return I*s; } template inline interval operator / (const T& s, const interval& I) { return interval(s,s)/I; } template std::ostream& operator << (std::ostream& os, const interval& I) { return os << '[' << I.get_lower_bound() << ',' << I.get_upper_bound() << ']'; } } }