Created
April 28, 2017 01:22
-
-
Save eddieantonio/ec9632c054cea7a574acf5c8c4c7789e to your computer and use it in GitHub Desktop.
A C++ interval class with test cases.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Defining an interval on integer and floating point number lines. | |
* | |
* Compile with: | |
* | |
* $ c++ -std=c++11 interval.cc -o interval | |
* | |
* Run tests: | |
* | |
* $ ./interval | |
* | |
*/ | |
#include <cassert> | |
#include <iostream> | |
#include <exception> | |
#include <cmath> | |
class BaseInterval { | |
public: | |
enum Location { Less = -1, Within = 0, Greater = 1 }; | |
}; | |
template <class T> | |
class Interval : public BaseInterval { | |
private: | |
const T _left, _right; | |
public: | |
Interval(T a, T b) | |
// Always set left as min and right as max to simplify assumptions. | |
: _left(std::min(a, b)), _right(std::max(a, b)) { } | |
/** | |
* Where is x relative to the interval? | |
*/ | |
Location where(T x) const { | |
if (x > right()) { | |
return Greater; | |
} else if (x < left()) { | |
return Less; | |
} else if ((x >= left()) && (x <= right())) { | |
return Within; | |
} | |
// It's NaN or incomparable. | |
throw std::domain_error("Cannot determine location of NaN"); | |
} | |
/*== Accessors ==*/ | |
/** | |
* Leftmost bound. | |
*/ | |
T left() const { | |
return _left; | |
} | |
/** | |
* Rightmost bound. | |
*/ | |
T right() const { | |
return _right; | |
} | |
}; | |
namespace { | |
template <class T> | |
void test_case() { | |
using Location = BaseInterval::Location; | |
// "ordinary" cases | |
{ | |
Interval<T> a(-1, 1); | |
assert(a.where(2) == Location::Greater); | |
} | |
{ | |
Interval<T> a(-1, 1); | |
assert(a.where(-2) == Location::Less); | |
} | |
{ | |
Interval<T> a(-1, 1); | |
assert(a.where(0) == Location::Within); | |
} | |
// boundary cases | |
{ | |
const auto lower = -1; | |
Interval<T> a(lower, 1); | |
assert(a.where(lower) == Location::Within); | |
} | |
{ | |
const auto upper = 1; | |
Interval<T> a(-1, upper); | |
assert(a.where(upper) == Location::Within); | |
} | |
// degenerate cases | |
{ | |
const auto point = 0; | |
Interval<T> a(point, point); | |
assert(a.where(point) == Location::Within); | |
} | |
{ | |
const auto point = 0; | |
Interval<T> a(point, point); | |
assert(a.where(point - 1) == Location::Less); | |
} | |
{ | |
const auto point = 0; | |
Interval<int> a(point, point); | |
assert(a.where(point + 1) == Location::Greater); | |
} | |
// "flipped" cases | |
{ | |
Interval<T> a(1, -1); | |
assert(a.where(2) == Location::Greater); | |
} | |
{ | |
Interval<T> a(1, -1); | |
assert(a.where(-2) == Location::Less); | |
} | |
{ | |
Interval<T> a(1, -1); | |
assert(a.where(0) == Location::Within); | |
} | |
// flipped boundary cases | |
{ | |
const auto lower = -1; | |
Interval<T> a(1, lower); | |
assert(a.where(lower) == Location::Within); | |
} | |
{ | |
const auto upper = 1; | |
Interval<T> a(upper, -1); | |
assert(a.where(upper) == Location::Within); | |
} | |
}; | |
template <class T> | |
void ieee754_test_cases() { | |
using Location = BaseInterval::Location; | |
// When bounds are infinite, we're always in the bounds. | |
{ | |
Interval<T> a(-INFINITY, 0); | |
assert(a.where(-INFINITY) == Location::Within); | |
} | |
{ | |
Interval<T> a(0, INFINITY); | |
assert(a.where(INFINITY) == Location::Within); | |
} | |
{ | |
Interval<T> a(-INFINITY, INFINITY); | |
assert(a.where(0) == Location::Within); | |
} | |
{ | |
Interval<T> a(INFINITY, -INFINITY); | |
assert(a.where(0) == Location::Within); | |
} | |
// Where is a NaN? Nowhere! | |
{ | |
Interval<double> a(-INFINITY, INFINITY); | |
try { | |
a.where(NAN); | |
assert(false); | |
} catch (std::domain_error &e) { | |
assert(true); | |
} | |
} | |
} | |
} | |
int main(int argc, char *argv[]) { | |
test_case<char>(); | |
test_case<short>(); | |
test_case<int>(); | |
test_case<long>(); | |
test_case<float>(); | |
test_case<double>(); | |
test_case<long double>(); | |
ieee754_test_cases<float>(); | |
ieee754_test_cases<double>(); | |
ieee754_test_cases<long double>(); | |
std::cout << "👍\n"; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment