Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion doc/tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,13 @@ mode there.
operator in both orders, and the relations, `set_contains()` and
`set_strictly_contains()` at 2^53 + 1 and 2^64 − 1, which the conversion to
a double made wrong; and that an integer that is a double gives what the
double gives. `misc` declares a `namespace detail` of its own, which
double gives. `relations` also checks `gaol::lexicographic_less`
(GAOL v5, point V): a strict total order that puts the empty set first and
the intervals by their bounds, which `<` is not, being the `strictPrecedes`
of IEEE 1788-2015, true as soon as either interval is empty; it sorts a
`std::vector` holding an empty interval, which `<` reads past the end of,
and fills a `std::set` with intervals that overlap, which `<` loses.
`misc` declares a `namespace detail` of its own, which
`using namespace gaol` made ambiguous while GAOL's helpers were in
`gaol_core::detail`, and checks the names of `gaol`, which names those of
`gaol_core` one by one (GAOL v5). `static_assert` checks that `gaol::f`
Expand Down
38 changes: 38 additions & 0 deletions gaol/gaol_interval.h
Original file line number Diff line number Diff line change
Expand Up @@ -2111,6 +2111,43 @@ GAOL_NODISCARD GAOL_INLINE bool operator>(T n, const interval &I)
return N.left() == N.right() ? N > I : N >= I;
}

/*!
lexicographic_less: a total order on the intervals, for the containers
and the algorithms of the standard library that ask one (std::set,
std::map, std::sort, std::max, std::min, std::clamp...), which < is
not: < is the certainly_le() of IEEE 1788-2015 (strictPrecedes), true
as soon as either interval is empty, so that std::set<interval> keeps
no two intervals that overlap and loses every interval once the empty
set is inserted, and std::sort reads past the end of a vector that
holds an empty interval (GAOL v5).

A class, which the standard library takes as a comparator:
std::set<interval, gaol::lexicographic_less>. The empty set comes
first, then the intervals by their left bound and by their right
bound, -oo of a left bound and +oo of a right one included, -0.0 and
+0.0 being equal as bounds. The order is strict: less(I, I) is false,
and two intervals of the same bounds, or two empty sets, are not less
than each other. Specializing std::less for
gaol::interval is left to the program (issue #70): a program passes
gaol::lexicographic_less to its containers explicitly.
*/
struct lexicographic_less {
GAOL_NODISCARD GAOL_INLINE
bool operator()(const interval &I1, const interval &I2) const
{
if (I1.is_empty() || I2.is_empty()) {
return I1.is_empty() && !I2.is_empty();
}
if (gaol_detail::quiet_less(I1.left(), I2.left())) {
return true;
}
if (gaol_detail::quiet_less(I2.left(), I1.left())) {
return false;
}
return gaol_detail::quiet_less(I1.right(), I2.right());
}
};

//@}

GAOL_INLINE interval&
Expand Down Expand Up @@ -2278,6 +2315,7 @@ namespace gaol {
using gaol_core::integer;
using gaol_core::invabs_rel;
using gaol_core::inverse;
using gaol_core::lexicographic_less;
using gaol_core::log;
using gaol_core::max;
using gaol_core::min;
Expand Down
2 changes: 2 additions & 0 deletions tests/misc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ namespace gaol_names
GAOL_NAMES_CLASS(log2_node);
GAOL_NAMES_CLASS(sign_node);
GAOL_NAMES_CLASS(trunc_node);
// The comparator of the containers of the standard library (GAOL v5, point V)
GAOL_NAMES_CLASS(lexicographic_less);
#undef GAOL_NAMES_CLASS

static_assert(same<decltype(gaol::version), const char *const>() && gaol::version_major == GAOL_MAJOR_VERSION,
Expand Down
64 changes: 64 additions & 0 deletions tests/relations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
// rather than CppUnit (GAOL v5)
#include "unit_tests.h"

#include <algorithm>
#include <climits>
#include <set>
#include <vector>

class relations_test {
public:
Expand Down Expand Up @@ -239,6 +242,66 @@ class relations_test {
}
}

/* lexicographic_less (GAOL v5, point V): a total order for the containers
of the standard library. < is strictPrecedes of IEEE 1788-2015, true as
soon as either interval is empty: a std::set<interval> built on < loses
the intervals that overlap, and std::sort reads past the end of a vector
that holds an empty interval, which the sort with lexicographic_less()
checks here, an empty interval sorted to its place, at the front of
the vector. */
void test_lexicographic_less() {
// Strict: no interval is less than itself
const interval xs[] = { interval::emptyset(), interval(-GAOL_INFINITY, 1),
interval(-GAOL_INFINITY, 2), interval::universe(),
interval(-1, 1), interval(0, 1), interval(0, 2),
interval(1, 1), interval(1, 2),
interval(1, GAOL_INFINITY) };
gaol::lexicographic_less const less;
gaol_core::lexicographic_less const less_core;
for (const interval& x : xs) {
TEST_FALSE(less(x, x));
TEST_FALSE(less_core(x, x));
}
for (size_t i = 0; i < sizeof(xs)/sizeof(xs[0]); ++i) {
for (size_t j = 0; j < sizeof(xs)/sizeof(xs[0]); ++j) {
// The expected order of the list above, and a strict order: for two
// different intervals exactly one of less(a, b) and less(b, a),
// for two equal intervals neither
TEST_TRUE(less(xs[i], xs[j]) == (i < j));
TEST_TRUE(less(xs[i], xs[j]) != less(xs[j], xs[i]) || i == j);
TEST_TRUE(!(less(xs[i], xs[j]) && less(xs[j], xs[i])));
}
}
// The empty set comes first
TEST_TRUE(less(interval::emptyset(), interval::universe()));
TEST_FALSE(less(interval::emptyset(), interval::emptyset()));
TEST_FALSE(less(interval::universe(), interval::emptyset()));
// -0.0 and +0.0 are equal as bounds
TEST_FALSE(less(interval(-0.0, 0.0), interval(0.0, 0.0)));
TEST_FALSE(less(interval(0.0, 0.0), interval(-0.0, 0.0)));

// A std::set keeps the intervals that overlap and the empty set
std::set<interval, gaol::lexicographic_less> s;
s.insert(interval(0, 1));
s.insert(interval(0.5, 3)); // overlaps the first: < loses it
s.insert(interval(1, 2)); // overlaps both
s.insert(interval::emptyset());
TEST_TRUE(s.size() == 4);
TEST_TRUE(s.count(interval(0.5, 3)) == 1);
TEST_TRUE(s.begin()->is_empty());
// std::sort of a vector holding an empty interval, which reads past the
// end of the vector with < (certainly_le)
std::vector<interval> v;
v.push_back(interval(1, 2));
v.push_back(interval::emptyset());
v.push_back(interval(-3, 0));
std::sort(v.begin(), v.end(), less);
TEST_TRUE(v.size() == 3);
TEST_TRUE(v[0].is_empty());
TEST_TRUE(less(v[1], v[2]));
TEST_TRUE(v[1].set_eq(interval(-3, 0)) && v[2].set_eq(interval(1, 2)));
}

// <-- End of tests
};

Expand All @@ -248,4 +311,5 @@ GAOL_UNIT_MAIN(relations_test, "relations",
GAOL_UNIT_TEST(test_certainly),
GAOL_UNIT_TEST(test_symbols_with_double),
GAOL_UNIT_TEST(test_symbols_with_integers),
GAOL_UNIT_TEST(test_lexicographic_less),
GAOL_UNIT_TEST(test_misc))
Loading