sm::interval

Minimum and maximum values

import sm.interval;

Module file: sm/interval.cppm.

Table of Contents

Summary

sm::interval is a class for specifying a mathematical interval between an infimum, min and a supremum, max. Its data consists of just two numbers indicating the minimum and maximum of the interval. By default the interval is closed [min, max], but you can use template parameters to define semi-open and open intervals.

It is used as a return object for the vec::range and vvec::range methods and gives semantic meaning to the two values min and max, which are public and accessible directly by client code (if a 2 element array were used for an interval, the client coder would have to remember if element 0 was min or max).

The interval object can participate in the process of determining the range of values in a data container and it can test for a value being within the interval.

Design

sm::interval takes one template argument, specifying the type of the values and two arguments defining the extrema

export namespace sm
{
    template <typename T,
              interval_endpoint infimum = interval_endpoint::closed,
              interval_endpoint supremum = interval_endpoint::closed>
    struct interval
    {

sm::interval is constexpr capable and may be used with scalar, std::complex or vector (sm::vec and std::array) values. Used with vector values, sm::interval may be used as a bounding box in graphics applications.

Construct

sm::interval<T> r;                              // Default (closed) interval has min == max == T{0}
sm::interval<T> r = { T{0}, T{10} };            // Construct with initializer list
sm::interval<T> r(T{0}, T{10});                 // Construct with a defined interval [0, 10]
sm::interval<T> r (sm::interval_init::for_search); // Construct ready for search...
sm::interval<T> r = sm::interval<T>::search_initialized(); // ...or initialize from a static function

I usually write this:

auto r = sm::interval<T>::search_initialized();

Set

Set the interval manually in a single function call

sm::interval<int> r;  // interval initially [0, 0]
r.set (-100, 100); // interval now [-100, 100]

or use initializer braces

r = { -100, 100 };

Update the interval to include a value

sm::interval<int> r; // interval initially 0 to 0
bool changed1 = r.update (100);      // interval now 0 to 100
bool changed2 = r.update (-100);     // interval now -100 to 100
bool changed3 = r.update (50);       // interval unchanged; still -100 to 100

update returns a bool which will be true if the interval was changed and false if the interval is not changed. In the example above, changed1 and changed2 will both be true, but changed3 will contain false.

You can add to and subtract from an interval

sm::interval<int> r = { -10, 10 };
r += 3;
std::cout << r << std::endl;  // [-7, 13]
sm::interval<int> r2 = r - 1;
std::cout << r2 << std::endl; // [-8, 12]

This works also for vector intervals:

sm::interval<sm::vec<float>> rv = { {0,0,0}, {1,1,1} };
std::cout << "interval:      " << rv << std::endl;                   // [(0,0,0), (1,1,1)]
std::cout << "interval + uz: " << rv + sm::vec<>::uz() << std::endl; // [(0,0,1), (1,1,2)]

Query

To query the max or min of the interval, just access the max or min members:

std::cout << "interval maximum is " << r.max << " and its minimum is " << r.min << std::endl;

You can stream the interval to get both at once:

std::cout << r << std::endl;

This would output [-100,100] in our previous example.

There’s a helper function to get interval.max - interval.min:

std::cout << "The interval 'spans': " << r.span() << std::endl;

The mid-point of the interval [(max - min) / 2] also has a helper:

std::cout << "The interval mid is " << r.mid() << std::endl;

Contains a value?

Test a value to see if the interval includes this value:

r.contains (45);     // would return bool true, following on from previous example
r.contains (-450);   // would return bool false

Contains another interval?

You can determine if one interval fits inside another with interval::contains():

sm::range<int> r1 = { 1, 100 };
sm::range<int> r2 = { 10, 90 };
sm::range<int> r3 = { -1, 2 };
std::cout << "range " << r1 << (r1.contains(r2) ? " contains " : " doesn't contain ") << r2 << std::endl;
std::cout << "range " << r1 << (r1.contains(r3) ? " contains " : " doesn't contain ") << r3 << std::endl;

Intersection

sm::range<int> r1 = { 1, 100 };
sm::range<int> r2 = { 90, 200 };

// Prints "r1 intersects r2":
std::cout << (r1.intersects (r2) ? "r1 intersects r2" : "no intersection") << std::endl;

Note that there is no interval<>::intersection (sm::interval<>& other) method to return the intersection between two intervals. This limitation results from the extrema types being defined at compile time (as template parameters), but runtime information is required to determine the extrema of the intersection result. For the same reason, there is no interval<>::union (sm::interval<>& other) method.

Comparison

You can compare intervals to be equal or not equal to each other

sm::interval<int> r1 = { 1, 100 };
sm::interval<int> r2 = { 10, 90 };
bool equality_test = (r1 == r2);
bool nonequality_test = (r1 != r2);

Determine the interval from data

Determine an interval from data. Here, we initialize an interval with min taking the maximum possible value for the type and max taking the minimum possible value. This is done with a call to interval::search_init. We then run through the data container, calling update for each element. For example:

sm::vvec<double> data (10, 0.0);
data.randomize();
sm::interval<double> r; // Default constructed interval is [ 0, 0 ]
r.search_init();     // prepare interval for search
for (auto d : data) { r.update (d); } // update on each element of data
std::cout << "The range of values in data was: " << r << std::endl;

To save a line of code, use the constructor for setting up an interval ready-configured for search:

sm::vvec<double> data;
data.randomize();
sm::interval<double> r (sm::interval_init::for_search); // avoids need for r.search_init()
for (auto d : data) { r.update (d); }

or use the static interval<>::search_initialized, which returns a suitable interval:

sm::vvec<double> data;
data.randomize();
auto r = sm::interval<double>::search_initialized();
for (auto d : data) { r.update (d); }

This site uses Just the Docs, a documentation theme for Jekyll.