58 : min_(std::move(lo)), max_(std::move(hi)), inclusion_(inc) {}
60 [[nodiscard]]
static std::expected<UnitRangeValue, RangeError>
61 make(
const bound_t& lo,
64 auto v = detail::validate_range<T>(lo.v, hi.v, inc);
66 return std::unexpected{v.error()};
68 return UnitRangeValue{lo, hi, inc};
71 [[nodiscard]]
static constexpr UnitRangeValue closed(bound_t lo, bound_t hi)
noexcept {
72 return UnitRangeValue{std::move(lo), std::move(hi), RangeInclusion::closed()};
74 [[nodiscard]]
static constexpr UnitRangeValue open(bound_t lo, bound_t hi)
noexcept {
75 return UnitRangeValue{std::move(lo), std::move(hi), RangeInclusion::open()};
77 [[nodiscard]]
static constexpr UnitRangeValue left_open(bound_t lo, bound_t hi)
noexcept {
78 return UnitRangeValue{std::move(lo), std::move(hi), RangeInclusion::left_open()};
80 [[nodiscard]]
static constexpr UnitRangeValue right_open(bound_t lo, bound_t hi)
noexcept {
81 return UnitRangeValue{std::move(lo), std::move(hi), RangeInclusion::right_open()};
84 [[nodiscard]]
constexpr bound_t min() const noexcept {
87 [[nodiscard]]
constexpr bound_t max() const noexcept {
90 [[nodiscard]]
constexpr RangeInclusion inclusion() const noexcept {
94 [[nodiscard]]
constexpr bool contains(
const bound_t& x)
const noexcept {
95 return detail::contains_value<T>(min_.v, max_.v, inclusion_, x.v);
98 [[nodiscard]]
constexpr bool contains(
const UnitRangeValue& other)
const noexcept {
101 const bool lo_ok = bound_inside_lower(other.min_.v, other.inclusion_.lower);
102 const bool hi_ok = bound_inside_upper(other.max_.v, other.inclusion_.upper);
103 return lo_ok && hi_ok;
106 [[nodiscard]]
constexpr bool overlaps(
const UnitRangeValue& other)
const noexcept {
108 if (max_.v < other.min_.v || other.max_.v < min_.v) {
111 if (max_.v == other.min_.v) {
112 return inclusion_.upper == Bound::Inclusive &&
113 other.inclusion_.lower == Bound::Inclusive;
115 if (other.max_.v == min_.v) {
116 return inclusion_.lower == Bound::Inclusive &&
117 other.inclusion_.upper == Bound::Inclusive;
122 [[nodiscard]]
constexpr std::optional<UnitRangeValue>
123 intersect(
const UnitRangeValue& other)
const noexcept {
124 if (!overlaps(other)) {
129 if (min_.v > other.min_.v) {
131 lo_inc = inclusion_.lower;
132 }
else if (min_.v < other.min_.v) {
134 lo_inc = other.inclusion_.lower;
138 (inclusion_.lower == Bound::Exclusive || other.inclusion_.lower == Bound::Exclusive)
144 if (max_.v < other.max_.v) {
146 hi_inc = inclusion_.upper;
147 }
else if (max_.v > other.max_.v) {
149 hi_inc = other.inclusion_.upper;
153 (inclusion_.upper == Bound::Exclusive || other.inclusion_.upper == Bound::Exclusive)
157 return UnitRangeValue{lo, hi, RangeInclusion{lo_inc, hi_inc}};
160 [[nodiscard]] std::string to_string()
const;
161 [[nodiscard]] std::string to_formatted_string()
const;
163 [[nodiscard]]
friend constexpr bool operator==(
const UnitRangeValue&,
164 const UnitRangeValue&) =
default;
167 [[nodiscard]]
constexpr bool bound_inside_lower(T other_v,
168 const Bound other_lower)
const noexcept {
169 if (inclusion_.lower == Bound::Inclusive) {
170 return other_v >= min_.v;
172 if (other_lower == Bound::Exclusive) {
173 return other_v >= min_.v;
175 return other_v > min_.v;
178 [[nodiscard]]
constexpr bool bound_inside_upper(T other_v,
179 const Bound other_upper)
const noexcept {
180 if (inclusion_.upper == Bound::Inclusive) {
181 return other_v <= max_.v;
183 if (other_upper == Bound::Exclusive) {
184 return other_v <= max_.v;
186 return other_v < max_.v;
191 RangeInclusion inclusion_{RangeInclusion::closed()};
199 using unit_t = M::base_unit_t;
207 : min_(std::move(lo)), max_(std::move(hi)), inclusion_(inc) {}
209 [[nodiscard]]
static std::expected<MeasureRangeValue, RangeError>
213 auto v = detail::validate_range<T>(lo.v, hi.v, inc);
215 return std::unexpected{v.error()};
221 return MeasureRangeValue{std::move(lo), std::move(hi), RangeInclusion::closed()};
227 return MeasureRangeValue{std::move(lo), std::move(hi), RangeInclusion::left_open()};
230 return MeasureRangeValue{std::move(lo), std::move(hi), RangeInclusion::right_open()};
233 [[nodiscard]]
constexpr bound_t min()
const noexcept {
236 [[nodiscard]]
constexpr bound_t max()
const noexcept {
239 [[nodiscard]]
constexpr RangeInclusion inclusion()
const noexcept {
243 [[nodiscard]]
constexpr bool contains(
const bound_t& x)
const noexcept {
244 return detail::contains_value<T>(min_.v, max_.v, inclusion_, x.v);
247 [[nodiscard]]
constexpr bool contains(
const MeasureRangeValue& other)
const noexcept {
248 return as_unit_range().contains(other.as_unit_range());
251 [[nodiscard]]
constexpr bool overlaps(
const MeasureRangeValue& other)
const noexcept {
252 return as_unit_range().overlaps(other.as_unit_range());
255 [[nodiscard]]
constexpr std::optional<MeasureRangeValue>
257 if (
auto r = as_unit_range().intersect(other.as_unit_range())) {
264 [[nodiscard]] std::string to_string()
const;
265 [[nodiscard]] std::string to_formatted_string()
const;