Files
stdex/include/stdex/interval.hpp
Simon Rozman 97e781778c
Some checks failed
Doxygen Action / build (push) Has been cancelled
interval: add extend and intersect
Signed-off-by: Simon Rozman <simon.rozman@amebis.si>
2025-12-16 17:41:58 +01:00

283 lines
5.7 KiB
C++

/*
SPDX-License-Identifier: MIT
Copyright © 2023-2025 Amebis
*/
#pragma once
#include "compat.hpp"
#include <vector>
namespace stdex
{
///
/// Numerical interval
///
template <class T>
struct interval
{
T start; ///< interval start
T end; ///< interval end
///
/// Constructs an invalid interval
///
interval() noexcept : start(static_cast<T>(1)), end(static_cast<T>(0)) {}
///
/// Constructs a zero-size interval
///
/// \param[in] x Interval start and end value
///
interval(_In_ T x) noexcept : start(x), end(x) {}
///
/// Constructs an interval
///
/// \param[in] _start Interval start value
/// \param[in] _end Interval end value
///
interval(_In_ T _start, _In_ T _end) noexcept : start(_start), end(_end) {}
///
/// Returns interval size
///
/// \returns Interval size or 0 if interval is invalid
///
T size() const { return start <= end ? end - start : 0; }
///
/// Is interval empty?
///
/// \returns true if interval is empty or false otherwise
///
bool empty() const { return start >= end; }
///
/// Invalidates interval
///
void invalidate()
{
start = static_cast<T>(1);
end = static_cast<T>(0);
}
///
/// Is interval valid?
///
/// \returns true if interval is valid or false otherwise
///
operator bool() const { return start <= end; }
///
/// Is value in interval?
///
/// \param[in] x Value to test
///
/// \returns true if x is in [start, end) or false otherwise
///
bool contains(_In_ T x) const { return start <= x && x < end; }
///
/// Adds two intervals by components
///
/// \param[in] other Second interval
///
/// \returns Resulting interval
///
interval operator+(_In_ const interval& other) const
{
return interval(start + other.start, end + other.end);
}
///
/// Moves interval towards the end by a number
///
/// \param[in] x Amount to move for
///
/// \returns Moved interval
///
interval operator+(_In_ const T x) const
{
return interval(start + x, end + x);
}
///
/// Moves interval towards the end by a number
///
/// \param[in] x Amount to move for
///
/// \returns Resulting interval
///
interval operator+=(_In_ const T x)
{
start += x;
end += x;
return *this;
}
///
/// Moves interval towards the end by one
///
/// \returns Moved interval
///
interval operator++()
{
++start;
++end;
return *this;
}
///
/// Moves interval towards the end by one
///
/// \returns Original interval
///
interval operator++(int) // Postfix increment operator.
{
interval r = *this;
++start;
++end;
return r;
}
///
/// Subtracts two intervals by components
///
/// \param[in] other Second interval
///
/// \returns Resulting interval
///
interval operator-(_In_ const interval& other) const
{
return interval(start - other.start, end - other.end);
}
///
/// Moves interval towards the beginning by a number
///
/// \param[in] x Amount to move for
///
/// \returns Moved interval
///
interval operator-(_In_ const T x) const
{
return interval(start - x, end - x);
}
///
/// Moves interval towards the beginning by a number
///
/// \param[in] x Amount to move for
///
/// \returns Resulting interval
///
interval operator-=(_In_ const T x)
{
start -= x;
end -= x;
return *this;
}
///
/// Moves interval towards the beginning by one
///
/// \returns Moved interval
///
interval operator--()
{
--start;
--end;
return *this;
}
///
/// Moves interval towards the begginning by one
///
/// \returns Original interval
///
interval operator--(int) // Postfix decrement operator.
{
interval r = *this;
--start;
--end;
return r;
}
///
/// Are intervals identical?
///
/// \param[in] other Second interval to compare
///
/// \returns true if intervals are identical or false otherwise
///
bool operator==(_In_ const interval& other) const
{
return start == other.start && end == other.end;
}
///
/// Are intervals different?
///
/// \param[in] other Second interval to compare
///
/// \returns true if intervals are different or false otherwise
///
bool operator!=(_In_ const interval& other) const
{
return !operator ==(other);
}
///
/// Extends the interval to another interval
///
/// \note Makes a union of two intervals, so it covers [min(start, other.start), max(end, other.end)).
/// When either of the intervals is empty, the result is the non-empty one.
/// When both intervals are empty, the result is an empty interval.
///
/// \param[in] other Interval to extend to
///
/// \returns Resulting interval
///
interval extend(_In_ const interval& other)
{
if (other.empty())
return *this;
if (empty())
return *this = other;
if (other.start < start)
start = other.start;
if (end < other.end)
end = other.end;
return *this;
}
///
/// Intersects the interval with another interval
///
/// \note Makes an intersection of two intervals, so it covers [max(start, other.start), min(end, other.end)).
/// When intervals do not interlap, the result is an invalid interval.
/// When any of the intervals is empty, the result is an invalid (and empty by definition) interval.
///
/// \param[in] other Interval to intersect with
///
/// \returns Resulting interval
///
interval intersect(_In_ const interval& other)
{
if (empty() || other.empty()) {
invalidate();
return *this;
}
if (start < other.start)
start = other.start;
if (other.end < end)
end = other.end;
return *this;
}
};
template <class T, class AX = std::allocator<interval<T>>>
using interval_vector = std::vector<interval<T>, AX>;
}