├── .gitignore ├── LICENSE.txt ├── README.md ├── bin ├── bump-version └── discover-tests ├── docs ├── _config.yml ├── index.md ├── interval-map.md ├── interval-set.md ├── interval.md └── version.md └── src ├── CMakeLists.txt ├── rs-interval.hpp ├── rs-interval ├── arithmetic.hpp ├── category-base-class.hpp ├── interval-base-class.hpp ├── interval.hpp ├── map.hpp ├── set.hpp ├── types.hpp └── version.hpp └── test ├── continuous-arithmetic-test.cpp ├── continuous-basic-test.cpp ├── continuous-boundary-addition-test.cpp ├── continuous-boundary-basic-test.cpp ├── continuous-boundary-comparison-test.cpp ├── continuous-boundary-multiplication-test.cpp ├── continuous-map-test.cpp ├── continuous-set-test.cpp ├── integral-arithmetic-test.cpp ├── integral-basic-test.cpp ├── integral-boundary-addition-test.cpp ├── integral-boundary-basic-test.cpp ├── integral-boundary-comparison-test.cpp ├── integral-boundary-multiplication-test.cpp ├── integral-map-test.cpp ├── integral-set-test.cpp ├── ordered-basic-test.cpp ├── ordered-boundary-basic-test.cpp ├── ordered-boundary-comparison-test.cpp ├── ordered-map-test.cpp ├── ordered-set-test.cpp ├── stepwise-basic-test.cpp ├── stepwise-boundary-basic-test.cpp ├── stepwise-boundary-comparison-test.cpp ├── stepwise-example.hpp ├── stepwise-map-test.cpp ├── stepwise-set-test.cpp ├── types-test.cpp ├── unit-test.cpp ├── unit-test.hpp └── version-test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Icon? 3 | build* 4 | _* 5 | *.bkp 6 | *.log 7 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Intervals Library 2 | 3 | By Ross Smith 4 | 5 | ## [Link to main documentation](https://captaincrowbar.github.io/rs-interval/) 6 | 7 | ## Overview 8 | 9 | ```c++ 10 | #include "rs-interval.hpp" 11 | namespace RS::Interval; 12 | ``` 13 | 14 | This library provides classes for the manipulation of intervals within an ordered type; for example, a range of integers or floating point values. Other ordered but non-arithmetic types, such as strings, are also supported. In addition to the basic interval type, it also supports sets of intervals, and maps from a series of intervals to a value type. 15 | 16 | Some simple examples: 17 | 18 | ```c++ 19 | Interval ii(10, 20); 20 | // Contains all integers from 10 to 20 21 | Interval di(0, 1, Bound::open); 22 | // Contains all values from 0 to 1, excluding 0 and 1 23 | Interval si("A", "B", Bound::closed, Bound::open); 24 | // Contains all strings starting with A 25 | ``` 26 | 27 | The library is header-only; there are no binaries to build or install. A CMake 28 | file is supplied to build the tests, but this is not required if you only 29 | want to use the library and not run the tests. Installing it simply requires 30 | copying the `"rs-interval"` directory and `"rs-interval.hpp"` header 31 | (these are under `"source"`) to somewhere on your include search path. The 32 | `install` option in the CMake file will do this (it may not work on Windows 33 | because of the lack of a standardized search path). 34 | 35 | Although this library provides arithmetic operators on intervals where they 36 | make sense, it is not intended to be a full interval arithmetic library in 37 | the sense used in scientific computing, since it does not include the special 38 | handling of floating point rounding issues required by such a library. 39 | 40 | You can include `"rs-interval.hpp"` to import the whole library, or a 41 | selection from the following headers if you don't need all of the library's 42 | features: 43 | 44 | * `"rs-interval/interval.hpp"` -- Interval class 45 | * `"rs-interval/interval-map.hpp"` -- Interval map class 46 | * `"rs-interval/interval-set.hpp"` -- Interval set class 47 | * `"rs-interval/version.hpp"` -- Version information 48 | 49 | The following principal classes are defined (in addition to a number of 50 | supporting infrastructure types): 51 | 52 | ```c++ 53 | template class Interval; 54 | ``` 55 | 56 | A class representing an interval over the underlying type `T`. The 57 | `IntervalCompatible` concept, indicating a valid underlying value type for an 58 | interval, is also defined in the `"interval.hpp"` header. 59 | 60 | The interval may be a finite interval with upper and lower bounds, bounded on 61 | one side but unbounded on the other, an empty interval (containing no 62 | values), or a universal interval (containing all values of the underlying 63 | type). Depending on the properties of the value type, the interval class may 64 | also distinguish between open, closed, and half-open intervals, indicating 65 | whether or not the interval includes neither, both, or one of its bounds. 66 | 67 | ```c++ 68 | template class IntervalSet; 69 | ``` 70 | 71 | This class represents an arbitrary, possibly discontinuous, subset of the 72 | domain of `T` as a set of intervals. This is an ordered set analogous to 73 | `std::set`. The intervals in the set are automatically updated when intervals 74 | are inserted or erased: an inserted interval will be merged with any existing 75 | intervals that it touches; an interval will be removed, reduced in size, or 76 | split into two if part of it is erased. 77 | 78 | ```c++ 79 | template class IntervalMap; 80 | ``` 81 | 82 | A map from a set of intervals over `K` to values of `T`. The `IntervalMap` 83 | object also contains a default value of `T` that will be returned when a key 84 | is not a member of any of the intervals in the map. 85 | 86 | This is an ordered map analogous to `std::map`. The intervals in the map are 87 | automatically updated when intervals are inserted or erased: an inserted 88 | interval will erase any parts of existing intervals that it covers, or will 89 | be merged with them if they have the same mapped value; an interval will be 90 | removed, reduced in size, or split into two if part of it is erased. 91 | -------------------------------------------------------------------------------- /bin/bump-version: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Increment the patch number in the project version 4 | 5 | set -o errexit 6 | set -o nounset 7 | set -o pipefail 8 | shopt -s globasciiranges globstar nullglob 9 | 10 | if [[ "${TRACE-}" ]]; then 11 | set -o xtrace 12 | fi 13 | 14 | bin="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)" 15 | root="$(readlink -f "$bin/..")" 16 | 17 | if [ "${root##*/}" != "rs-interval" ]; then 18 | echo "*** Project root not found" 19 | exit 1 20 | fi 21 | 22 | cd "$root" 23 | 24 | cmakefile=src/CMakeLists.txt 25 | header=src/rs-interval/version.hpp 26 | temp=__TEMP__ 27 | 28 | version=$(grep -E ' VERSION \d+\.\d+\.\d+' "$cmakefile" \ 29 | | head -n 1 \ 30 | | grep -Eo '[0-9.]+') 31 | 32 | if ! [[ "$version" ]]; then 33 | echo "*** Version number not found" 34 | exit 1 35 | fi 36 | 37 | v1=${version%%.*} 38 | v2=${version#*.} 39 | v2=${v2%.*} 40 | v3=${version##*.} 41 | (( ++v3 )) 42 | 43 | sed -E 's/(VERSION) [0-9]+\.[0-9]+\.[0-9]+/\1 '"$v1.$v2.$v3/" \ 44 | "$cmakefile" > "$temp" 45 | mv "$temp" "$cmakefile" 46 | sed -E 's/(return) ([{"]+ ?)[0-9]+([ .,]+)[0-9]+([ .,])[0-9]+( ?[}"]+);/\1 \2'"$v1"'\3'"$v2"'\4'"$v3"'\5;/' \ 47 | "$header" > "$temp" 48 | mv "$temp" "$header" 49 | -------------------------------------------------------------------------------- /bin/discover-tests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Run this to update the test harness if any unit tests have been changed 4 | 5 | set -o errexit 6 | set -o nounset 7 | set -o pipefail 8 | shopt -s globasciiranges globstar nullglob 9 | 10 | if [[ "${TRACE-}" ]]; then 11 | set -o xtrace 12 | fi 13 | 14 | bin="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)" 15 | root="$(readlink -f "$bin/..")" 16 | 17 | if [ "${root##*/}" != "rs-interval" ]; then 18 | echo "*** Project root not found" 19 | exit 1 20 | fi 21 | 22 | src="$root/src" 23 | 24 | if ! [ -d "$src" ]; then 25 | echo "*** Source directory not found: $src" 26 | exit 1 27 | fi 28 | 29 | cmake_file="$src/CMakeLists.txt" 30 | test_harness="$src/test/unit-test.cpp" 31 | 32 | test_files=$(grep -E '^\s*test/.+-test.cpp' $cmake_file \ 33 | | grep -Fv 'test/unit-test.cpp' \ 34 | | sed -E "s!test/!$src/&!") 35 | 36 | test_functions=$(grep -Eh '^void [a-z0-9_]+\(\)' $test_files \ 37 | | sed -E 's!void ([a-z0-9_]+)\(\).*!\1!') 38 | 39 | cat << EOF > $test_harness 40 | // This file is generated by the discover-tests script 41 | 42 | #include "test/unit-test.hpp" 43 | 44 | using namespace RS::UnitTest; 45 | 46 | EOF 47 | 48 | for f in $test_functions; do 49 | echo "void $f();" >> $test_harness 50 | done 51 | 52 | cat << EOF >> $test_harness 53 | 54 | int main(int argc, char** argv) { 55 | 56 | main_args = std::vector(argv + 1, argv + argc); 57 | std::println(""); 58 | std::println("{}Running unit tests{}", xhead, xreset); 59 | std::println("{}{}{}", xrule, rule, xreset); 60 | 61 | EOF 62 | 63 | for f in $test_functions; do 64 | echo " call_me_maybe($f, \"$f\");" >> $test_harness 65 | done 66 | 67 | cat << EOF >> $test_harness 68 | 69 | std::println("{}{}{}", xrule, rule, xreset); 70 | 71 | if (failures == 0) { 72 | std::println("{}OK - all tests passed{}", xpass, xreset); 73 | } else { 74 | std::println("{}*** Test failures: {}{}", xfail, failures, xreset); 75 | } 76 | 77 | std::println(""); 78 | 79 | return failures; 80 | 81 | } 82 | EOF 83 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | markdown: kramdown 2 | theme: jekyll-theme-midnight 3 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Intervals Library 2 | 3 | By Ross Smith 4 | 5 | _[GitHub repository](https://github.com/CaptainCrowbar/rs-interval)_ 6 | 7 | ## Overview 8 | 9 | ```c++ 10 | #include "rs-interval.hpp" 11 | namespace RS::Interval; 12 | ``` 13 | 14 | This library provides classes for the manipulation of intervals within an 15 | ordered type; for example, a range of integers or floating point values. 16 | Other ordered but non-arithmetic types, such as strings, are also supported. 17 | In addition to the basic interval type, it also supports sets of intervals, 18 | and maps from a series of intervals to a value type. 19 | 20 | Some simple examples: 21 | 22 | ```c++ 23 | Interval ii(10, 20); 24 | // Contains all integers from 10 to 20 25 | Interval di(0, 1, Bound::open); 26 | // Contains all values from 0 to 1, excluding 0 and 1 27 | Interval si("A", "B", Bound::closed, Bound::open); 28 | // Contains all strings starting with A 29 | ``` 30 | 31 | The library is header-only; there are no binaries to build or install. A CMake 32 | file is supplied to build the tests, but this is not required if you only 33 | want to use the library and not run the tests. Installing it simply requires 34 | copying the `"rs-interval"` directory and `"rs-interval.hpp"` header 35 | (these are under `"src"`) to somewhere on your include search path. The 36 | `install` option in the CMake file will do this (it may not work on Windows 37 | because of the lack of a standardized search path). 38 | 39 | Although this library provides arithmetic operators on intervals where they 40 | make sense, it is not intended to be a full interval arithmetic library in 41 | the sense used in scientific computing, since it does not include the special 42 | handling of floating point rounding issues required by such a library. 43 | 44 | You can include `"rs-interval.hpp"` to import the whole library, or a 45 | selection from the following headers if you don't need all of the library's 46 | features: 47 | 48 | * `"rs-interval/interval.hpp"` -- [Interval class](interval.html) 49 | * `"rs-interval/interval-map.hpp"` -- [Interval map class](interval-map.html) 50 | * `"rs-interval/interval-set.hpp"` -- [Interval set class](interval-set.html) 51 | * `"rs-interval/version.hpp"` -- [Version information](version.html) 52 | 53 | The following principal classes are defined (in addition to a number of 54 | supporting infrastructure types): 55 | 56 | ```c++ 57 | template class Interval; 58 | ``` 59 | 60 | A class representing an interval over the underlying type `T`. The 61 | `IntervalCompatible` concept, indicating a valid underlying value type for an 62 | interval, is also defined in the `"interval.hpp"` header. 63 | 64 | The interval may be a finite interval with upper and lower bounds, bounded on 65 | one side but unbounded on the other, an empty interval (containing no 66 | values), or a universal interval (containing all values of the underlying 67 | type). Depending on the properties of the value type, the interval class may 68 | also distinguish between open, closed, and half-open intervals, indicating 69 | whether or not the interval includes neither, both, or one of its bounds. 70 | 71 | ```c++ 72 | template class IntervalSet; 73 | ``` 74 | 75 | This class represents an arbitrary, possibly discontinuous, subset of the 76 | domain of `T` as a set of intervals. This is an ordered set analogous to 77 | `std::set`. The intervals in the set are automatically updated when intervals 78 | are inserted or erased: an inserted interval will be merged with any existing 79 | intervals that it touches; an interval will be removed, reduced in size, or 80 | split into two if part of it is erased. 81 | 82 | ```c++ 83 | template class IntervalMap; 84 | ``` 85 | 86 | A map from a set of intervals over `K` to values of `T`. The `IntervalMap` 87 | object also contains a default value of `T` that will be returned when a key 88 | is not a member of any of the intervals in the map. 89 | 90 | This is an ordered map analogous to `std::map`. The intervals in the map are 91 | automatically updated when intervals are inserted or erased: an inserted 92 | interval will erase any parts of existing intervals that it covers, or will 93 | be merged with them if they have the same mapped value; an interval will be 94 | removed, reduced in size, or split into two if part of it is erased. 95 | -------------------------------------------------------------------------------- /docs/interval-map.md: -------------------------------------------------------------------------------- 1 | # Interval Map Class 2 | 3 | _[Interval Library by Ross Smith](index.html)_ 4 | 5 | ```c++ 6 | #include "rs-interval/interval-map.hpp" 7 | namespace RS::Interval; 8 | ``` 9 | 10 | This header defines a class representing a map from a set of disjoint 11 | intervals to values of some other type. 12 | 13 | ## Contents 14 | 15 | * TOC 16 | {:toc} 17 | 18 | ## Interval map 19 | 20 | ```c++ 21 | template class IntervalMap; 22 | ``` 23 | 24 | A map from a set of intervals over `K` to values of `T`. The `IntervalMap` 25 | object also contains a default value of `T` that will be returned when a key 26 | is not a member of any of the intervals in the map. 27 | 28 | This is an ordered map analogous to `std::map`. The intervals in the map are 29 | automatically updated when intervals are inserted or erased: an inserted 30 | interval will erase any parts of existing intervals that it covers, or will 31 | be merged with them if they have the same mapped value; an interval will be 32 | removed, reduced in size, or split into two if part of it is erased. 33 | 34 | ### Member types 35 | 36 | ```c++ 37 | using IntervalMap::key_type = K; 38 | using IntervalMap::interval_type = Interval; 39 | using IntervalMap::mapped_type = T; 40 | using IntervalMap::value_type = std::pair, T>; 41 | ``` 42 | 43 | Type aliases. 44 | 45 | ```c++ 46 | class IntervalMap::iterator; 47 | ``` 48 | 49 | A bidirectional `const` iterator over the intervals that make up the set, 50 | dereferencing to an `Interval`. 51 | 52 | ### Member constants 53 | 54 | ```c++ 55 | static constexpr Category IntervalMap::category = interval_category; 56 | ``` 57 | 58 | The underlying key type's interval category. 59 | 60 | ### Life cycle functions 61 | 62 | ```c++ 63 | IntervalMap::IntervalMap(); 64 | explicit IntervalMap::IntervalMap(const T& defval); 65 | IntervalMap::IntervalMap(std::initializer_list list); 66 | IntervalMap::IntervalMap(const IntervalMap& map); 67 | IntervalMap::IntervalMap(IntervalMap&& map) noexcept; 68 | IntervalMap::~IntervalMap() noexcept; 69 | IntervalMap& IntervalMap::operator=(const IntervalMap& map); 70 | IntervalMap& IntervalMap::operator=(IntervalMap&& map) noexcept; 71 | ``` 72 | 73 | An optional default value can be provided; if none is provided, a default 74 | constructed value of `T` is used. When a map is constructed from a list of 75 | `(interval,value)` pairs, the intervals are ordered lexicographically, and 76 | adjacent intervals are merged when they touch or overlap and have the same 77 | mapped value. When two intervals overlap but do not have the same mapped 78 | value, later entries in the initializer list will overwrite earlier ones. 79 | 80 | ### Comparison operators 81 | 82 | ```c++ 83 | std::strong_ordering 84 | operator<=>(const IntervalMap& a, const IntervalMap& b) noexcept; 85 | bool operator==(const IntervalMap& a, const IntervalMap& b) noexcept; 86 | bool operator!=(const IntervalMap& a, const IntervalMap& b) noexcept; 87 | bool operator<(const IntervalMap& a, const IntervalMap& b) noexcept; 88 | bool operator>(const IntervalMap& a, const IntervalMap& b) noexcept; 89 | bool operator<=(const IntervalMap& a, const IntervalMap& b) noexcept; 90 | bool operator>=(const IntervalMap& a, const IntervalMap& b) noexcept; 91 | ``` 92 | 93 | Lexicographical comparison operators. These call `T`'s comparison operators. 94 | The default value does not participate in comparisons. 95 | 96 | ### Element access functions 97 | 98 | ```c++ 99 | const T& IntervalMap::operator[](const K& key) const; 100 | ``` 101 | 102 | Returns the mapped value corresponding to the interval containing the given 103 | key, or the default value if no interval contains the key. 104 | 105 | ```c++ 106 | IntervalMap::iterator IntervalMap::begin() const noexcept; 107 | IntervalMap::iterator IntervalMap::end() const noexcept; 108 | ``` 109 | 110 | Iterators over the map's list of `(interval,value)` pairs. 111 | 112 | ```c++ 113 | IntervalMap::iterator IntervalMap::find(const K& key) const; 114 | ``` 115 | 116 | Returns an iterator pointing to the interval containing the given key, or 117 | `end()` if no such interval exists. 118 | 119 | ```c++ 120 | IntervalMap::iterator IntervalMap::lower_bound(const K& key) const; 121 | IntervalMap::iterator IntervalMap::upper_bound(const K& key) const; 122 | ``` 123 | 124 | If the key is contained in one of the intervals in the map, `lower_bound()` 125 | returns an iterator pointing to that interval, and `upper_bound()` returns the 126 | next iterator. If not, both functions return the iterator pointing to the 127 | first interval after the given key, or `end()` if no such interval exists. 128 | 129 | ### Query functions 130 | 131 | ```c++ 132 | bool IntervalMap::contains(const K& key) const; 133 | ``` 134 | 135 | True if one of the intervals in the map contains the key. 136 | 137 | ```c++ 138 | const T& IntervalMap::default_value() const noexcept; 139 | ``` 140 | 141 | Returns the default value. 142 | 143 | ```c++ 144 | bool IntervalMap::empty() const noexcept; 145 | ``` 146 | 147 | True if the map is empty. 148 | 149 | ```c++ 150 | std::size_t IntervalMap::size() const noexcept; 151 | ``` 152 | 153 | Returns the number of intervals in the map. 154 | 155 | ```c++ 156 | std::size_t IntervalMap::hash() const noexcept; 157 | struct std::hash; 158 | ``` 159 | 160 | Hash function for an interval map. 161 | 162 | ### Modifying functions 163 | 164 | ```c++ 165 | void IntervalMap::default_value(const T& defval); 166 | ``` 167 | 168 | Changes the default value. 169 | 170 | ```c++ 171 | void IntervalMap::clear() noexcept; 172 | void IntervalMap::reset(const T& defval = T()); 173 | ``` 174 | 175 | These clear all `(interval,value)` pairs from the map. The `clear()` function 176 | does not change the default value, while `reset()` also changes it. 177 | 178 | ```c++ 179 | void IntervalMap::insert(const Interval& in, const T& t); 180 | void IntervalMap::insert(const value_type& v); 181 | ``` 182 | 183 | Adds a new `(interval,value)` pair to the map. Intervals in the map that 184 | overlap this interval will be modified or removed as necessary. 185 | 186 | ```c++ 187 | void IntervalMap::erase(const Interval& in); 188 | ``` 189 | 190 | Removes an interval from the map. This does not have to exactly match an 191 | interval already in the set. Intervals in the map that overlap this interval 192 | will be modified or removed as necessary. This will have no effect if this 193 | interval does not overlap any existing interval in the map. 194 | 195 | ```c++ 196 | void IntervalMap::swap(IntervalMap& map) noexcept; 197 | void swap(IntervalMap& a, IntervalMap& b) noexcept; 198 | ``` 199 | 200 | Swap two interval maps. 201 | 202 | ### Formatters 203 | 204 | ```c++ 205 | template 206 | requires (std::formattable && std::formattable) 207 | struct std::formatter>; 208 | ``` 209 | 210 | Standard formatter for interval maps. This does not implement any format 211 | specifiers; it will always use the default format for the key and value 212 | types. The format is `"{A:X,B:Y,C:Z,...}",` where `A, B, C,` etc are 213 | intervals or values of `K,` and `X, Y, Z,` etc are values of `T.` 214 | -------------------------------------------------------------------------------- /docs/interval-set.md: -------------------------------------------------------------------------------- 1 | # Interval Set Class 2 | 3 | _[Interval Library by Ross Smith](index.html)_ 4 | 5 | ```c++ 6 | #include "rs-interval/interval-set.hpp" 7 | namespace RS::Interval; 8 | ``` 9 | 10 | This header defines a class representing a set of disjoint intervals. 11 | 12 | ## Contents 13 | 14 | * TOC 15 | {:toc} 16 | 17 | ## Interval set class 18 | 19 | ```c++ 20 | template class IntervalSet; 21 | ``` 22 | 23 | This class represents a subset of `T`'s domain as a set of intervals. This is 24 | an ordered set analogous to `std::set`. The intervals in the set are 25 | automatically updated when intervals are inserted or erased: an inserted 26 | interval will be merged with any existing intervals that it touches; an 27 | interval will be removed, reduced in size, or split into two if part of it is 28 | erased. 29 | 30 | ### Member types 31 | 32 | ```c++ 33 | using IntervalSet::interval_type = Interval; 34 | using IntervalSet::value_type = T; 35 | ``` 36 | 37 | Type aliases. 38 | 39 | ```c++ 40 | class IntervalSet::iterator; 41 | ``` 42 | 43 | A bidirectional `const` iterator over the intervals that make up the set, 44 | dereferencing to an `Interval`. 45 | 46 | ### Member constants 47 | 48 | ```c++ 49 | static constexpr Category IntervalSet::category 50 | = interval_category; 51 | ``` 52 | 53 | The underlying value type's interval category. 54 | 55 | ### Life cycle functions 56 | 57 | ```c++ 58 | IntervalSet::IntervalSet(); 59 | IntervalSet::IntervalSet(const T& t); 60 | IntervalSet::IntervalSet(const Interval& in); 61 | IntervalSet::IntervalSet(std::initializer_list list); 62 | IntervalSet::IntervalSet(const IntervalSet& set); 63 | IntervalSet::IntervalSet(IntervalSet&& set) noexcept; 64 | IntervalSet::~IntervalSet() noexcept; 65 | IntervalSet& IntervalSet::operator=(const IntervalSet& set); 66 | IntervalSet& IntervalSet::operator=(IntervalSet&& set) noexcept; 67 | ``` 68 | 69 | When a set is constructed from a list of intervals or values, the intervals 70 | are ordered lexicographically, and adjacent intervals are merged when they 71 | touch or overlap. 72 | 73 | ### Comparison operators 74 | 75 | ```c++ 76 | std::strong_ordering 77 | operator<=>(const IntervalSet& a, const IntervalSet& b) noexcept; 78 | bool operator==(const IntervalSet& a, const IntervalSet& b) noexcept; 79 | bool operator!=(const IntervalSet& a, const IntervalSet& b) noexcept; 80 | bool operator<(const IntervalSet& a, const IntervalSet& b) noexcept; 81 | bool operator>(const IntervalSet& a, const IntervalSet& b) noexcept; 82 | bool operator<=(const IntervalSet& a, const IntervalSet& b) noexcept; 83 | bool operator>=(const IntervalSet& a, const IntervalSet& b) noexcept; 84 | ``` 85 | 86 | Lexicographical comparison operators. These call `T`'s comparison operators. 87 | 88 | ### Iterator functions 89 | 90 | ```c++ 91 | IntervalSet::iterator IntervalSet::begin() const noexcept; 92 | IntervalSet::iterator IntervalSet::end() const noexcept; 93 | ``` 94 | 95 | Iterators over the intervals in the set. 96 | 97 | ### Query functions 98 | 99 | ```c++ 100 | bool IntervalSet::contains(const T& t) const; 101 | ``` 102 | 103 | True if the value is an element of any of the intervals in the set. 104 | 105 | ```c++ 106 | bool IntervalSet::empty() const noexcept; 107 | ``` 108 | 109 | True if the set is empty. 110 | 111 | ```c++ 112 | std::size_t IntervalSet::size() const noexcept; 113 | ``` 114 | 115 | Returns the number of intervals in the set. 116 | 117 | ```c++ 118 | std::size_t IntervalSet::hash() const noexcept; 119 | struct std::hash; 120 | ``` 121 | 122 | Hash function for an interval set. 123 | 124 | ### Modifying functions 125 | 126 | ```c++ 127 | void IntervalSet::clear() noexcept; 128 | ``` 129 | 130 | Clears all intervals from the set, leaving it empty. 131 | 132 | ```c++ 133 | void IntervalSet::insert(const Interval& in); 134 | ``` 135 | 136 | Adds a new interval to the set. Adjacent intervals are merged when they touch 137 | or overlap. 138 | 139 | ```c++ 140 | void IntervalSet::erase(const Interval& in); 141 | ``` 142 | 143 | Removes an interval from the set. This does not have to exactly match an 144 | interval already in the set. Intervals in the set that overlap this interval 145 | will be modified or removed as necessary. This will have no effect if this 146 | interval does not overlap any existing interval in the set. 147 | 148 | ```c++ 149 | void IntervalSet::swap(IntervalSet& set) noexcept; 150 | void swap(IntervalSet& a, IntervalSet& b) noexcept; 151 | ``` 152 | 153 | Swap two interval sets. 154 | 155 | ### Set operations 156 | 157 | ```c++ 158 | IntervalSet IntervalSet::complement() const; 159 | IntervalSet& IntervalSet::apply_complement(); 160 | ``` 161 | 162 | Returns the complement of the set, i.e. a set whose member intervals contain 163 | every value of `T` that is not in this set. The `apply_complement()` function 164 | modifies the set in place. 165 | 166 | ```c++ 167 | IntervalSet IntervalSet::set_intersection(const IntervalSet& b) const; 168 | IntervalSet IntervalSet::set_intersection(const Interval& b) const; 169 | IntervalSet IntervalSet::set_intersection(const T& b) const; 170 | IntervalSet IntervalSet::set_union(const IntervalSet& b) const; 171 | IntervalSet IntervalSet::set_union(const Interval& b) const; 172 | IntervalSet IntervalSet::set_union(const T& b) const; 173 | IntervalSet IntervalSet::set_difference(const IntervalSet& b) const; 174 | IntervalSet IntervalSet::set_difference(const Interval& b) const; 175 | IntervalSet IntervalSet::set_difference(const T& b) const; 176 | IntervalSet IntervalSet::set_symmetric_difference(const IntervalSet& b) const; 177 | IntervalSet IntervalSet::set_symmetric_difference(const Interval& b) const; 178 | IntervalSet IntervalSet::set_symmetric_difference(const T& b) const; 179 | ``` 180 | 181 | Binary set theoretic operations. 182 | 183 | ```c++ 184 | IntervalSet& IntervalSet::apply_intersection(const IntervalSet& b); 185 | IntervalSet& IntervalSet::apply_intersection(const Interval& b); 186 | IntervalSet& IntervalSet::apply_intersection(const T& b); 187 | IntervalSet& IntervalSet::apply_union(const IntervalSet& b); 188 | IntervalSet& IntervalSet::apply_union(const Interval& b); 189 | IntervalSet& IntervalSet::apply_union(const T& b); 190 | IntervalSet& IntervalSet::apply_difference(const IntervalSet& b); 191 | IntervalSet& IntervalSet::apply_difference(const Interval& b); 192 | IntervalSet& IntervalSet::apply_difference(const T& b); 193 | IntervalSet& IntervalSet::apply_symmetric_difference(const IntervalSet& b); 194 | IntervalSet& IntervalSet::apply_symmetric_difference(const Interval& b); 195 | IntervalSet& IntervalSet::apply_symmetric_difference(const T& b); 196 | ``` 197 | 198 | In-place versions of the set theoretic operations. 199 | 200 | ```c++ 201 | IntervalSet set_intersection(const IntervalSet& a, const IntervalSet& b); 202 | IntervalSet set_intersection(const IntervalSet& a, const Interval& b); 203 | IntervalSet set_intersection(const Interval& a, const IntervalSet& b); 204 | IntervalSet set_intersection(const IntervalSet& a, const T& b); 205 | IntervalSet set_intersection(const T& a, const IntervalSet& b); 206 | IntervalSet set_intersection(const Interval& a, const Interval& b); 207 | IntervalSet set_intersection(const Interval& a, const T& b); 208 | IntervalSet set_intersection(const T& a, const Interval& b); 209 | IntervalSet set_intersection(const T& a, const T& b); 210 | IntervalSet set_union(const IntervalSet& a, const IntervalSet& b); 211 | IntervalSet set_union(const IntervalSet& a, const Interval& b); 212 | IntervalSet set_union(const Interval& a, const IntervalSet& b); 213 | IntervalSet set_union(const IntervalSet& a, const T& b); 214 | IntervalSet set_union(const T& a, const IntervalSet& b); 215 | IntervalSet set_union(const Interval& a, const Interval& b); 216 | IntervalSet set_union(const Interval& a, const T& b); 217 | IntervalSet set_union(const T& a, const Interval& b); 218 | IntervalSet set_union(const T& a, const T& b); 219 | IntervalSet set_difference(const IntervalSet& a, const IntervalSet& b); 220 | IntervalSet set_difference(const IntervalSet& a, const Interval& b); 221 | IntervalSet set_difference(const Interval& a, const IntervalSet& b); 222 | IntervalSet set_difference(const IntervalSet& a, const T& b); 223 | IntervalSet set_difference(const T& a, const IntervalSet& b); 224 | IntervalSet set_difference(const Interval& a, const Interval& b); 225 | IntervalSet set_difference(const Interval& a, const T& b); 226 | IntervalSet set_difference(const T& a, const Interval& b); 227 | IntervalSet set_difference(const T& a, const T& b); 228 | IntervalSet set_symmetric_difference(const IntervalSet& a, const IntervalSet& b); 229 | IntervalSet set_symmetric_difference(const IntervalSet& a, const Interval& b); 230 | IntervalSet set_symmetric_difference(const Interval& a, const IntervalSet& b); 231 | IntervalSet set_symmetric_difference(const IntervalSet& a, const T& b); 232 | IntervalSet set_symmetric_difference(const T& a, const IntervalSet& b); 233 | IntervalSet set_symmetric_difference(const Interval& a, const Interval& b); 234 | IntervalSet set_symmetric_difference(const Interval& a, const T& b); 235 | IntervalSet set_symmetric_difference(const T& a, const Interval& b); 236 | IntervalSet set_symmetric_difference(const T& a, const T& b); 237 | ``` 238 | 239 | Free function versions of the set theoretic operations. 240 | 241 | ### Formatters 242 | 243 | ```c++ 244 | template 245 | requires (std::formattable) 246 | struct std::formatter>; 247 | ``` 248 | 249 | Standard formatter for interval sets. This accepts the same format 250 | specification as `std::formatter.` The format is `"{A,B,C...}",` where `A, 251 | B, C`, etc are intervals or values of `T,` formatted using the `Interval` 252 | formatter. 253 | -------------------------------------------------------------------------------- /docs/version.md: -------------------------------------------------------------------------------- 1 | # Version Information 2 | 3 | _[Interval Library by Ross Smith](index.html)_ 4 | 5 | ```c++ 6 | #include "rs-interval/version.hpp" 7 | namespace RS::Interval; 8 | ``` 9 | 10 | This header supplies library version information. 11 | 12 | ```c++ 13 | std::array version() noexcept; 14 | ``` 15 | 16 | Returns an array containing the major, minor, and patch version numbers. 17 | 18 | ```c++ 19 | std::string version_string(); 20 | ``` 21 | 22 | Returns the version numbers as a string (e.g. `"1.23.456"`). 23 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | # set(CMAKE_VERBOSE_MAKEFILE ON) 3 | 4 | project(IntervalsLibrary 5 | VERSION 0.3.8 6 | LANGUAGES CXX 7 | ) 8 | 9 | set(CMAKE_CXX_STANDARD 23) 10 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 11 | set(CMAKE_CXX_EXTENSIONS OFF) 12 | 13 | if(MSVC) 14 | add_compile_definitions(_CRT_SECURE_NO_WARNINGS=1) 15 | add_compile_options(/EHsc /Gy /MP /O2 /sdl /utf-8 /W4 /WX) 16 | else() 17 | set(THREADS_PREFER_PTHREAD_FLAG TRUE) 18 | add_compile_options(-fdiagnostics-color=always -finput-charset=UTF-8 -march=native -O2 -Wall -Wextra -Wpedantic -Werror) 19 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") 20 | add_compile_options(-Wsuggest-override -Wsuggest-destructor-override) 21 | endif() 22 | endif() 23 | 24 | if(WIN32) 25 | add_compile_definitions(NOMINMAX=1 UNICODE=1 _UNICODE=1 WINVER=0xa00 _WIN32_WINNT=0xa00) 26 | else() 27 | add_compile_definitions(_REENTRANT=1 _XOPEN_SOURCE=700) 28 | if(APPLE) 29 | add_compile_definitions(_DARWIN_C_SOURCE=1) 30 | endif() 31 | endif() 32 | 33 | find_package(Threads REQUIRED) 34 | include_directories(.) 35 | set(library rs-interval) 36 | set(unittest test-${library}) 37 | 38 | add_executable(${unittest} 39 | test/types-test.cpp 40 | test/continuous-arithmetic-test.cpp 41 | test/continuous-basic-test.cpp 42 | test/continuous-boundary-addition-test.cpp 43 | test/continuous-boundary-basic-test.cpp 44 | test/continuous-boundary-comparison-test.cpp 45 | test/continuous-boundary-multiplication-test.cpp 46 | test/continuous-map-test.cpp 47 | test/continuous-set-test.cpp 48 | test/integral-arithmetic-test.cpp 49 | test/integral-basic-test.cpp 50 | test/integral-boundary-addition-test.cpp 51 | test/integral-boundary-basic-test.cpp 52 | test/integral-boundary-comparison-test.cpp 53 | test/integral-boundary-multiplication-test.cpp 54 | test/integral-map-test.cpp 55 | test/integral-set-test.cpp 56 | test/ordered-basic-test.cpp 57 | test/ordered-boundary-basic-test.cpp 58 | test/ordered-boundary-comparison-test.cpp 59 | test/ordered-map-test.cpp 60 | test/ordered-set-test.cpp 61 | test/stepwise-basic-test.cpp 62 | test/stepwise-boundary-basic-test.cpp 63 | test/stepwise-boundary-comparison-test.cpp 64 | test/stepwise-example.hpp 65 | test/stepwise-map-test.cpp 66 | test/stepwise-set-test.cpp 67 | test/version-test.cpp 68 | test/unit-test.cpp 69 | ) 70 | 71 | target_link_libraries(${unittest} 72 | PRIVATE Threads::Threads 73 | ) 74 | 75 | install(FILES ${library}.hpp DESTINATION include) 76 | install(DIRECTORY ${library} DESTINATION include PATTERN "*.cpp" EXCLUDE) 77 | -------------------------------------------------------------------------------- /src/rs-interval.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "rs-interval/arithmetic.hpp" 4 | #include "rs-interval/category-base-class.hpp" 5 | #include "rs-interval/interval-base-class.hpp" 6 | #include "rs-interval/interval.hpp" 7 | #include "rs-interval/map.hpp" 8 | #include "rs-interval/set.hpp" 9 | #include "rs-interval/types.hpp" 10 | #include "rs-interval/version.hpp" 11 | -------------------------------------------------------------------------------- /src/rs-interval/arithmetic.hpp: -------------------------------------------------------------------------------- 1 | // This header is private to the implementation and should not be included by users 2 | 3 | #pragma once 4 | 5 | #include "rs-interval/category-base-class.hpp" 6 | #include "rs-interval/interval-base-class.hpp" 7 | #include "rs-interval/types.hpp" 8 | #include 9 | #include 10 | 11 | namespace RS::Interval { 12 | 13 | template class Interval; 14 | template class IntervalSet; 15 | 16 | namespace Detail { 17 | 18 | template 19 | bool contains_zero(const Interval& i) noexcept { 20 | 21 | if (i.empty()) { 22 | return false; 23 | } 24 | 25 | bool right_zero = i.right() == Bound::unbound 26 | || (i.right() == Bound::closed && i.max() >= T{}) 27 | || (i.right() == Bound::open && i.max() > T{}); 28 | 29 | if (! right_zero) { 30 | return false; 31 | } 32 | 33 | bool left_zero = i.left() == Bound::unbound 34 | || (i.left() == Bound::closed && i.min() <= T{}) 35 | || (i.left() == Bound::open && i.min() < T{}); 36 | 37 | return left_zero; 38 | 39 | } 40 | 41 | template 42 | Boundary left_boundary_of(const Interval& i) { 43 | if (i.empty()) { 44 | return {}; 45 | } else if (i.is_left_closed()) { 46 | return {i.min(), BoundaryType::closed}; 47 | } else if (i.is_left_open()) { 48 | return {i.min(), BoundaryType::open}; 49 | } else { 50 | return {{}, BoundaryType::minus_infinity}; 51 | } 52 | } 53 | 54 | template 55 | Boundary right_boundary_of(const Interval& i) { 56 | if (i.empty()) { 57 | return {}; 58 | } else if (i.is_right_closed()) { 59 | return {i.max(), BoundaryType::closed}; 60 | } else if (i.is_right_open()) { 61 | return {i.max(), BoundaryType::open}; 62 | } else { 63 | return {{}, BoundaryType::plus_infinity}; 64 | } 65 | } 66 | 67 | template 68 | Interval interval_from_boundaries(const Boundary& l, const Boundary& r) { 69 | 70 | static constexpr auto convert_bound = [] (BoundaryType t) constexpr { 71 | switch (t) { 72 | case BoundaryType::empty: return Bound::empty; 73 | case BoundaryType::open: return Bound::open; 74 | case BoundaryType::closed: return Bound::closed; 75 | default: return Bound::unbound; 76 | } 77 | }; 78 | 79 | auto lbound = convert_bound(l.type); 80 | auto rbound = convert_bound(r.type); 81 | 82 | return {l.value, r.value, lbound, rbound}; 83 | 84 | } 85 | 86 | template 87 | Interval reciprocal_interval(const Interval& i) { 88 | 89 | if (i.empty()) { 90 | return {}; 91 | } 92 | 93 | T lvalue {}; 94 | T rvalue {}; 95 | Bound lbound, rbound; 96 | 97 | if (i.left() == Bound::unbound) { 98 | rbound = Bound::open; 99 | } else if (i.min() == T{}) { 100 | rbound = Bound::unbound; 101 | } else { 102 | rvalue = static_cast(1) / i.min(); 103 | rbound = i.left(); 104 | } 105 | 106 | if (i.right() == Bound::unbound) { 107 | lbound = Bound::open; 108 | } else if (i.max() == T{}) { 109 | lbound = Bound::unbound; 110 | } else { 111 | lvalue = static_cast(1) / i.max(); 112 | lbound = i.right(); 113 | } 114 | 115 | return {lvalue, rvalue, lbound, rbound}; 116 | 117 | } 118 | 119 | template 120 | IntervalSet reciprocal_set(const Interval& i) { 121 | 122 | if (i.empty()) { 123 | 124 | return {}; 125 | 126 | } else if (contains_zero(i)) { 127 | 128 | Interval negative_part(i.min(), {}, i.left(), Bound::open); 129 | Interval positive_part({}, i.max(), Bound::open, i.right()); 130 | auto negative_reciprocal = reciprocal_interval(negative_part); 131 | auto positive_reciprocal = reciprocal_interval(positive_part); 132 | 133 | return {negative_reciprocal, positive_reciprocal}; 134 | 135 | } else { 136 | 137 | return reciprocal_interval(i); 138 | 139 | } 140 | 141 | } 142 | 143 | } 144 | 145 | // Interval arithmetic operators 146 | 147 | template 148 | Interval operator+(const Interval& i) { 149 | return i; 150 | } 151 | 152 | template 153 | Interval operator-(const Interval& i) { 154 | return Interval(- i.max(), - i.min(), i.right(), i.left()); 155 | } 156 | 157 | template 158 | Interval operator+(const Interval& a, const Interval& b) { 159 | 160 | using namespace Detail; 161 | 162 | if (a.empty() || b.empty()) { 163 | return {}; 164 | } 165 | 166 | auto l = left_boundary_of(a) + left_boundary_of(b); 167 | auto r = right_boundary_of(a) + right_boundary_of(b); 168 | 169 | return interval_from_boundaries(l, r); 170 | 171 | } 172 | 173 | template 174 | Interval operator-(const Interval& a, const Interval& b) { 175 | return a + - b; 176 | } 177 | 178 | template 179 | Interval operator*(const Interval& a, const Interval& b) { 180 | 181 | using namespace Detail; 182 | 183 | if (a.empty() || b.empty()) { 184 | return {}; 185 | } 186 | 187 | auto al = left_boundary_of(a); 188 | auto ar = right_boundary_of(a); 189 | auto bl = left_boundary_of(b); 190 | auto br = right_boundary_of(b); 191 | 192 | std::array, 4> bounds { 193 | al * bl, 194 | al * br, 195 | ar * bl, 196 | ar * br, 197 | }; 198 | 199 | auto i = std::min_element(bounds.begin(), bounds.end(), 200 | [] (auto& a, auto& b) { return a.compare_ll(b); }); 201 | auto j = std::max_element(bounds.begin(), bounds.end(), 202 | [] (auto& a, auto& b) { return a.compare_rr(b); }); 203 | 204 | return interval_from_boundaries(*i, *j); 205 | 206 | } 207 | 208 | template 209 | IntervalSet operator/(const Interval& a, const Interval& b) { 210 | 211 | using namespace Detail; 212 | 213 | if (a.empty() || b.empty()) { 214 | return {}; 215 | } 216 | 217 | auto b_reciprocals = reciprocal_set(b); 218 | IntervalSet set; 219 | 220 | for (const auto& br: b_reciprocals) { 221 | set.insert(a * br); 222 | } 223 | 224 | return set; 225 | 226 | } 227 | 228 | template 229 | Interval operator+(const Interval& a, const T& b) { 230 | return a + Interval(b); 231 | } 232 | 233 | template 234 | Interval operator-(const Interval& a, const T& b) { 235 | return a - Interval(b); 236 | } 237 | 238 | template 239 | Interval operator*(const Interval& a, const T& b) { 240 | return a * Interval(b); 241 | } 242 | 243 | template 244 | IntervalSet operator/(const Interval& a, const T& b) { 245 | return a / Interval(b); 246 | } 247 | 248 | template 249 | Interval operator+(const T& a, const Interval& b) { 250 | return Interval(a) + b; 251 | } 252 | 253 | template 254 | Interval operator-(const T& a, const Interval& b) { 255 | return Interval(a) - b; 256 | } 257 | 258 | template 259 | Interval operator*(const T& a, const Interval& b) { 260 | return Interval(a) * b; 261 | } 262 | 263 | template 264 | IntervalSet operator/(const T& a, const Interval& b) { 265 | return Interval(a) / b; 266 | } 267 | 268 | template 269 | Interval& operator+=(Interval& a, const Interval& b) { 270 | return a = a + b; 271 | } 272 | 273 | template 274 | Interval& operator-=(Interval& a, const Interval& b) { 275 | return a = a - b; 276 | } 277 | 278 | template 279 | Interval& operator*=(Interval& a, const Interval& b) { 280 | return a = a * b; 281 | } 282 | 283 | template 284 | Interval& operator+=(Interval& a, const T& b) { 285 | return a = a + Interval(b); 286 | } 287 | 288 | template 289 | Interval& operator-=(Interval& a, const T& b) { 290 | return a = a - Interval(b); 291 | } 292 | 293 | template 294 | Interval& operator*=(Interval& a, const T& b) { 295 | return a = a * Interval(b); 296 | } 297 | 298 | } 299 | -------------------------------------------------------------------------------- /src/rs-interval/category-base-class.hpp: -------------------------------------------------------------------------------- 1 | // This header is private to the implementation and should not be included by users 2 | 3 | #pragma once 4 | 5 | #include "rs-interval/interval-base-class.hpp" 6 | #include "rs-interval/types.hpp" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace RS::Interval { 15 | 16 | namespace Detail { 17 | 18 | template 19 | concept SelfArithmeticIntervalType = requires (T t) { 20 | { t + t } -> std::convertible_to; 21 | { t - t } -> std::convertible_to; 22 | }; 23 | 24 | template 25 | concept IntArithmeticIntervalType = requires (T t) { 26 | { t + 1 } -> std::convertible_to; 27 | { t - 1 } -> std::convertible_to; 28 | }; 29 | 30 | template 31 | concept PtrdiffArithmeticIntervalType = IntArithmeticIntervalType 32 | && (sizeof(T) > sizeof(int)); 33 | 34 | template 35 | concept RandomAccessIntervalType = SelfArithmeticIntervalType 36 | || IntArithmeticIntervalType; 37 | 38 | template 39 | concept DifferenceIntervalType = requires (T t) { 40 | { t - t } -> std::convertible_to; 41 | }; 42 | 43 | } 44 | 45 | // Base class for intervals in the same category 46 | 47 | template > 48 | class IntervalCategoryBase; 49 | 50 | template 51 | class IntervalCategoryBase: 52 | public IntervalTypeBase {}; 53 | 54 | template 55 | class IntervalCategoryBase: 56 | public IntervalTypeBase { 57 | 58 | private: 59 | 60 | using delta_type = std::conditional_t, T, 61 | std::conditional_t, std::ptrdiff_t, int>>; 62 | 63 | public: 64 | 65 | class iterator { 66 | 67 | public: 68 | 69 | using difference_type = delta_type; 70 | using iterator_category = std::conditional_t, 71 | std::random_access_iterator_tag, std::bidirectional_iterator_tag>; 72 | using pointer = const T*; 73 | using reference = const T&; 74 | using value_type = T; 75 | 76 | iterator() = default; 77 | explicit iterator(T t): value_(t) {} 78 | 79 | const T& operator*() const noexcept { return value_; } 80 | const T* operator->() const noexcept { return &value_; } 81 | iterator& operator++() { ++value_; return *this; } 82 | iterator operator++(int) { auto old = *this; ++value_; return old; } 83 | iterator& operator--() { --value_; return *this; } 84 | iterator operator--(int) { auto old = *this; --value_; return old; } 85 | iterator& operator+=(delta_type rhs) { value_ = value_ + delta_type(rhs); return *this; } 86 | iterator& operator-=(delta_type rhs) { value_ = value_ - delta_type(rhs); return *this; } 87 | iterator operator+(delta_type rhs) const { auto it = *this; it += rhs; return it; } 88 | friend iterator operator+(delta_type lhs, const iterator& rhs) { return rhs + lhs; } 89 | iterator operator-(delta_type rhs) const { auto it = *this; it -= rhs; return it; } 90 | delta_type operator-(const iterator& rhs) { return delta_type(value_ - rhs.value_); } 91 | auto operator<=>(const iterator& rhs) const noexcept = default; 92 | 93 | private: 94 | 95 | T value_; 96 | 97 | }; 98 | 99 | iterator begin() const { return this->empty() ? iterator() : iterator(this->min_); } 100 | iterator end() const { return this->empty() ? iterator() : std::next(iterator(this->max_)); } 101 | std::size_t size() const; 102 | 103 | protected: 104 | 105 | void adjust_bounds(); 106 | 107 | }; 108 | 109 | template 110 | std::size_t IntervalCategoryBase::size() const { 111 | if (this->empty()) { 112 | return 0; 113 | } else if (this->is_infinite()) { 114 | return npos; 115 | } else if constexpr (Detail::DifferenceIntervalType) { 116 | return std::size_t(this->max_ - this->min_) + 1; 117 | } else { 118 | auto n = 1uz; 119 | for (T t = this->min_; t != this->max_; ++t, ++n) {} 120 | return n; 121 | } 122 | } 123 | 124 | template 125 | void IntervalCategoryBase::adjust_bounds() { 126 | if (this->left_ == Bound::open) { 127 | ++this->min_; 128 | this->left_ = Bound::closed; 129 | } 130 | if (this->right_ == Bound::open) { 131 | --this->max_; 132 | this->right_ = Bound::closed; 133 | } 134 | IntervalTypeBase::adjust_bounds(); 135 | } 136 | 137 | template 138 | class IntervalCategoryBase: 139 | public IntervalTypeBase { 140 | 141 | private: 142 | 143 | using delta_type = std::conditional_t< 144 | Detail::SelfArithmeticIntervalType, T, 145 | std::conditional_t, std::ptrdiff_t, int> 146 | >; 147 | 148 | public: 149 | 150 | class iterator { 151 | 152 | public: 153 | 154 | using difference_type = delta_type; 155 | using iterator_category = std::conditional_t, 156 | std::random_access_iterator_tag, std::bidirectional_iterator_tag>; 157 | using pointer = const T*; 158 | using reference = const T&; 159 | using value_type = T; 160 | 161 | iterator() = default; 162 | explicit iterator(T t): value_(t) {} 163 | 164 | const T& operator*() const noexcept { return value_; } 165 | const T* operator->() const noexcept { return &value_; } 166 | iterator& operator++() { ++value_; return *this; } 167 | iterator operator++(int) { auto old = *this; ++value_; return old; } 168 | iterator& operator--() { --value_; return *this; } 169 | iterator operator--(int) { auto old = *this; --value_; return old; } 170 | iterator& operator+=(delta_type rhs) { value_ = value_ + delta_type(rhs); return *this; } 171 | iterator& operator-=(delta_type rhs) { value_ = value_ - delta_type(rhs); return *this; } 172 | iterator operator+(delta_type rhs) const { auto it = *this; it += rhs; return it; } 173 | friend iterator operator+(delta_type lhs, const iterator& rhs) { return rhs + lhs; } 174 | iterator operator-(delta_type rhs) const { auto it = *this; it -= rhs; return it; } 175 | delta_type operator-(const iterator& rhs) { return delta_type(value_ - rhs.value_); } 176 | auto operator<=>(const iterator& rhs) const noexcept = default; 177 | 178 | private: 179 | 180 | T value_; 181 | 182 | }; 183 | 184 | iterator begin() const { return this->empty() ? iterator() : iterator(this->min_); } 185 | iterator end() const { return this->empty() ? iterator() : std::next(iterator(this->max_)); } 186 | std::size_t size() const; 187 | 188 | protected: 189 | 190 | void adjust_bounds(); 191 | 192 | }; 193 | 194 | template 195 | std::size_t IntervalCategoryBase::size() const { 196 | if (this->empty()) { 197 | return 0; 198 | } else if (this->is_infinite()) { 199 | return npos; 200 | } else if constexpr (Detail::DifferenceIntervalType) { 201 | return std::size_t(this->max_ - this->min_) + 1; 202 | } else { 203 | std::size_t n = 1; 204 | for (T t = this->min_; t != this->max_; ++t, ++n) {} 205 | return n; 206 | } 207 | } 208 | 209 | template 210 | void IntervalCategoryBase::adjust_bounds() { 211 | if (this->left_ == Bound::open) { 212 | ++this->min_; 213 | this->left_ = Bound::closed; 214 | } 215 | if (this->right_ == Bound::open) { 216 | --this->max_; 217 | this->right_ = Bound::closed; 218 | } 219 | IntervalTypeBase::adjust_bounds(); 220 | } 221 | 222 | template 223 | class IntervalCategoryBase: 224 | public IntervalTypeBase { 225 | public: 226 | T size() const { 227 | if (this->is_infinite()) { 228 | return std::numeric_limits::infinity(); 229 | } else { 230 | return this->max_ - this->min_; 231 | } 232 | } 233 | }; 234 | 235 | } 236 | -------------------------------------------------------------------------------- /src/rs-interval/interval-base-class.hpp: -------------------------------------------------------------------------------- 1 | // This header is private to the implementation and should not be included by users 2 | 3 | #pragma once 4 | 5 | #include "rs-interval/types.hpp" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace RS::Interval { 13 | 14 | namespace Detail { 15 | 16 | constexpr std::size_t hash_mix(std::size_t h1, std::size_t h2) noexcept { 17 | return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2)); 18 | } 19 | 20 | } 21 | 22 | // Base class for intervals with the same value type 23 | 24 | template 25 | class IntervalTypeBase { 26 | 27 | public: 28 | 29 | const T& min() const noexcept { return min_; } 30 | const T& max() const noexcept { return max_; } 31 | Bound left() const noexcept { return left_; } 32 | Bound right() const noexcept { return right_; } 33 | bool empty() const noexcept { return left_ == Bound::empty; } 34 | bool is_single() const noexcept { return left_ == Bound::closed && right_ == Bound::closed && min_ == max_; } 35 | bool is_range() const noexcept { return left_ == Bound::unbound || right_ == Bound::unbound || min_ != max_; } 36 | bool is_finite() const noexcept { return is_left_bounded() && is_right_bounded(); } 37 | bool is_infinite() const noexcept { return left_ == Bound::unbound || right_ == Bound::unbound; } 38 | bool is_universal() const noexcept { return left_ == Bound::unbound && right_ == Bound::unbound; } 39 | bool is_left_bounded() const noexcept { return is_left_open() || is_left_closed(); } 40 | bool is_left_closed() const noexcept { return left_ == Bound::closed; } 41 | bool is_left_open() const noexcept { return left_ == Bound::open; } 42 | bool is_right_bounded() const noexcept { return is_right_open() || is_right_closed(); } 43 | bool is_right_closed() const noexcept { return right_ == Bound::closed; } 44 | bool is_right_open() const noexcept { return right_ == Bound::open; } 45 | std::enable_if_t, std::size_t> hash() const noexcept; 46 | Match match(const T& t) const; 47 | 48 | protected: 49 | 50 | T min_ {}; 51 | T max_ {}; 52 | Bound left_ = Bound::empty; 53 | Bound right_ = Bound::empty; 54 | 55 | void adjust_bounds(); 56 | void do_swap(IntervalTypeBase& in) noexcept; 57 | 58 | }; 59 | 60 | template 61 | std::enable_if_t, std::size_t> 62 | IntervalTypeBase::hash() const noexcept { 63 | using namespace Detail; 64 | std::size_t h = std::hash()(min_); 65 | h = hash_mix(h, std::hash()(max_)); 66 | h = hash_mix(h, std::hash()(int(left_))); 67 | h = hash_mix(h, std::hash()(int(right_))); 68 | return h; 69 | } 70 | 71 | template 72 | Match IntervalTypeBase::match(const T& t) const { 73 | if (empty()) return Match::empty; 74 | else if (is_universal()) return Match::ok; 75 | else if (left_ == Bound::closed && t < min_) return Match::low; 76 | else if (left_ == Bound::open && t <= min_) return Match::low; 77 | else if (right_ == Bound::closed && t > max_) return Match::high; 78 | else if (right_ == Bound::open && t >= max_) return Match::high; 79 | else return Match::ok; 80 | } 81 | 82 | template 83 | void IntervalTypeBase::adjust_bounds() { 84 | 85 | if ((left_ == Bound::empty) != (right_ == Bound::empty)) { 86 | 87 | throw std::invalid_argument("Inconsistent interval bounds"); 88 | 89 | } else if (left_ == Bound::empty) { 90 | 91 | min_ = max_ = T{}; 92 | 93 | } else { 94 | 95 | if (is_finite()) { 96 | if (min_ > max_) { 97 | left_ = right_ = Bound::empty; 98 | } else if (min_ == max_ && (left_ == Bound::open || right_ == Bound::open)) { 99 | left_ = right_ = Bound::empty; 100 | } 101 | } 102 | 103 | if (! is_left_bounded()) { 104 | min_ = T{}; 105 | } 106 | 107 | if (! is_right_bounded()) { 108 | max_ = T{}; 109 | } 110 | 111 | } 112 | 113 | } 114 | 115 | template 116 | void IntervalTypeBase::do_swap(IntervalTypeBase& in) noexcept { 117 | using std::swap; 118 | swap(min_, in.min_); 119 | swap(max_, in.max_); 120 | swap(left_, in.left_); 121 | swap(right_, in.right_); 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/rs-interval/map.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "rs-interval/interval.hpp" 4 | #include "rs-interval/types.hpp" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace RS::Interval { 16 | 17 | // Interval map 18 | 19 | template 20 | class IntervalMap { 21 | 22 | public: 23 | 24 | using key_type = K; 25 | using mapped_type = T; 26 | using interval_type = Interval; 27 | using iterator = typename std::map, T>::const_iterator; 28 | using value_type = typename std::map, T>::value_type; 29 | 30 | static constexpr auto category = interval_category; 31 | 32 | IntervalMap() = default; 33 | explicit IntervalMap(const T& defval): map_(), def_(defval) {} 34 | IntervalMap(std::initializer_list list) { for (const auto& v: list) insert(v); } 35 | 36 | const T& operator[](const K& key) const; 37 | 38 | auto begin() const noexcept { return map_.begin(); } 39 | auto end() const noexcept { return map_.end(); } 40 | bool empty() const noexcept { return map_.empty(); } 41 | std::size_t size() const noexcept { return map_.size(); } 42 | const T& default_value() const noexcept { return def_; } 43 | void default_value(const T& defval) { def_ = defval; } 44 | bool contains(const K& key) const { return do_find(key).second; } 45 | iterator find(const K& key) const; 46 | iterator lower_bound(const K& key) const { return do_find(key).first; } 47 | iterator upper_bound(const K& key) const; 48 | void clear() noexcept { map_.clear(); } 49 | void reset(const T& defval = {}) { def_ = defval; clear(); } 50 | void insert(const interval_type& in, const T& t); 51 | void insert(const value_type& v) { insert(v.first, v.second); } 52 | void erase(const interval_type& in); 53 | void swap(IntervalMap& map) noexcept { map_.swap(map.map_); std::swap(def_, map.def_); } 54 | 55 | private: 56 | 57 | std::map, T> map_; 58 | T def_ {}; 59 | 60 | std::pair do_find(const K& key) const; 61 | 62 | }; 63 | 64 | template 65 | const T& IntervalMap::operator[](const K& key) const { 66 | auto [it,ok] = do_find(key); 67 | return ok ? it->second : def_; 68 | } 69 | 70 | template 71 | typename IntervalMap::iterator IntervalMap::find(const K& key) const { 72 | auto [it,ok] = do_find(key); 73 | return ok ? it : end(); 74 | } 75 | 76 | template 77 | typename IntervalMap::iterator IntervalMap::upper_bound(const K& key) const { 78 | auto [it,ok] = do_find(key); 79 | if (ok) { 80 | ++it; 81 | } 82 | return it; 83 | } 84 | 85 | template 86 | void IntervalMap::insert(const interval_type& in, const T& t) { 87 | 88 | if (in.empty()) { 89 | return; 90 | } 91 | 92 | auto key = in; 93 | auto i = map_.upper_bound(key); 94 | 95 | if (i != map_.begin()) { 96 | --i; 97 | } 98 | 99 | std::vector add; 100 | std::vector del; 101 | 102 | for (; i != map_.end(); ++i) { 103 | auto ord = key.order(i->first); 104 | if (ord <= Order::a_below_b) { 105 | break; 106 | } else if (ord <= Order::b_touches_a && i->second == t) { 107 | key = key.envelope(i->first); 108 | del.push_back(i); 109 | } else if (ord == Order::b_touches_a) { 110 | break; 111 | } else if (ord <= Order::b_overlaps_a) { 112 | auto diff = i->first.set_difference(in); 113 | for (const auto& d: diff) 114 | add.push_back({d, i->second}); 115 | del.push_back(i); 116 | } 117 | } 118 | 119 | for (const auto& d: del) { 120 | map_.erase(d); 121 | } 122 | 123 | for (const auto& a: add) { 124 | map_.insert(a); 125 | } 126 | 127 | map_.insert({key, t}); 128 | 129 | } 130 | 131 | template 132 | void IntervalMap::erase(const interval_type& in) { 133 | 134 | if (empty() || in.empty()) { 135 | return; 136 | } 137 | 138 | auto i = map_.lower_bound(in); 139 | 140 | if (i != map_.begin()) { 141 | --i; 142 | } 143 | 144 | std::vector vec; 145 | 146 | while (i != map_.end()) { 147 | auto ord = in.order(i->first); 148 | if (ord <= Order::a_touches_b) { 149 | break; 150 | } 151 | auto j = i++; 152 | if (ord <= Order::b_overlaps_a) { 153 | auto temp = j->first.set_difference(in); 154 | for (const auto& t: temp) { 155 | vec.push_back({t, j->second}); 156 | } 157 | map_.erase(j); 158 | } 159 | } 160 | 161 | map_.insert(vec.begin(), vec.end()); 162 | 163 | } 164 | 165 | template 166 | std::pair::iterator, bool> IntervalMap::do_find(const K& key) const { 167 | 168 | if (empty()) { 169 | return {end(), false}; 170 | } 171 | 172 | interval_type in(key); 173 | auto it = map_.lower_bound(in); 174 | 175 | if (it != map_.begin()) { 176 | --it; 177 | } 178 | 179 | for (; it != map_.end(); ++it) { 180 | auto m = it->first.match(key); 181 | if (m != Match::high) { 182 | return {it, m == Match::ok}; 183 | } 184 | } 185 | 186 | return {end(), false}; 187 | 188 | } 189 | 190 | template 191 | bool operator==(const IntervalMap& a, const IntervalMap& b) noexcept { 192 | return std::equal(a.begin(), a.end(), b.begin(), b.end()); 193 | } 194 | 195 | template 196 | auto operator<=>(const IntervalMap& a, const IntervalMap& b) noexcept { 197 | 198 | auto i = a.begin(); 199 | auto j = a.end(); 200 | auto k = b.begin(); 201 | auto l = b.end(); 202 | 203 | for (; i != j && k != l; ++i, ++j) { 204 | auto c = *i <=> *k; 205 | if (c != 0) { 206 | return c; 207 | } 208 | } 209 | 210 | if (*i != *j) { 211 | return std::strong_ordering::greater; 212 | } else if (*k != *l) { 213 | return std::strong_ordering::less; 214 | } else { 215 | return std::strong_ordering::equal; 216 | } 217 | 218 | } 219 | 220 | template 221 | void swap(IntervalMap& a, IntervalMap& b) noexcept { 222 | a.swap(b); 223 | } 224 | 225 | } 226 | 227 | template 228 | requires (std::formattable && std::formattable) 229 | struct std::formatter> { 230 | 231 | std::formatter> key_interval_formatter; 232 | std::formatter value_formatter; 233 | 234 | constexpr auto parse(std::format_parse_context& ctx) { 235 | return ctx.begin(); 236 | } 237 | 238 | template 239 | auto format(const RS::Interval::IntervalMap& map, FormatContext& ctx) const { 240 | 241 | auto out = ctx.out(); 242 | *out++ = '{'; 243 | 244 | if (! map.empty()) { 245 | 246 | auto in = map.begin(); 247 | auto end = map.end(); 248 | out = key_interval_formatter.format(in->first, ctx); 249 | *out++ = ':'; 250 | out = value_formatter.format(in->second, ctx); 251 | ++in; 252 | 253 | while (in != end) { 254 | *out++ = ','; 255 | out = key_interval_formatter.format(in->first, ctx); 256 | *out++ = ':'; 257 | out = value_formatter.format(in->second, ctx); 258 | ++in; 259 | } 260 | 261 | } 262 | 263 | *out++ = '}'; 264 | 265 | return out; 266 | 267 | } 268 | 269 | }; 270 | -------------------------------------------------------------------------------- /src/rs-interval/version.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace RS::Interval { 7 | 8 | inline std::array version() noexcept { 9 | return {0, 3, 0}; 10 | } 11 | 12 | inline std::string version_string() { 13 | return "0.3.8"; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/test/continuous-boundary-addition-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/types.hpp" 2 | #include "test/unit-test.hpp" 3 | 4 | using namespace RS::Interval; 5 | using namespace RS::Interval::Detail; 6 | 7 | using B = Boundary; 8 | using BT = BoundaryType; 9 | 10 | void test_rs_interval_continuous_boundary_inversion() { 11 | 12 | static const B none = {0, BT::empty}; 13 | static const B ninf = {0, BT::minus_infinity}; 14 | static const B pinf = {0, BT::plus_infinity}; 15 | static const B cl2 = {2, BT::closed}; 16 | static const B op2 = {2, BT::open}; 17 | static const B cl_2 = {-2, BT::closed}; 18 | static const B op_2 = {-2, BT::open}; 19 | 20 | TEST_EQUAL(- none, none); 21 | TEST_EQUAL(- ninf, pinf); 22 | TEST_EQUAL(- pinf, ninf); 23 | TEST_EQUAL(- cl2, cl_2); 24 | TEST_EQUAL(- op2, op_2); 25 | TEST_EQUAL(- cl_2, cl2); 26 | TEST_EQUAL(- op_2, op2); 27 | 28 | } 29 | 30 | void test_rs_interval_continuous_boundary_addition() { 31 | 32 | static const B none = {0, BT::empty}; 33 | static const B ninf = {0, BT::minus_infinity}; 34 | static const B pinf = {0, BT::plus_infinity}; 35 | static const B cl1 = {1, BT::closed}; 36 | static const B op1 = {1, BT::open}; 37 | static const B cl2 = {2, BT::closed}; 38 | static const B op2 = {2, BT::open}; 39 | static const B cl3 = {3, BT::closed}; 40 | static const B op3 = {3, BT::open}; 41 | 42 | TEST_EQUAL(none + none, none); 43 | TEST_EQUAL(none + ninf, none); 44 | TEST_EQUAL(none + pinf, none); 45 | TEST_EQUAL(none + cl2, none); 46 | TEST_EQUAL(none + op2, none); 47 | TEST_EQUAL(ninf + none, none); 48 | TEST_EQUAL(ninf + ninf, ninf); 49 | TEST_EQUAL(ninf + cl2, ninf); 50 | TEST_EQUAL(ninf + op2, ninf); 51 | TEST_EQUAL(pinf + none, none); 52 | TEST_EQUAL(pinf + pinf, pinf); 53 | TEST_EQUAL(pinf + cl2, pinf); 54 | TEST_EQUAL(cl1 + none, none); 55 | TEST_EQUAL(cl1 + ninf, ninf); 56 | TEST_EQUAL(cl1 + pinf, pinf); 57 | TEST_EQUAL(cl1 + cl2, cl3); 58 | TEST_EQUAL(cl1 + op2, op3); 59 | TEST_EQUAL(op1 + none, none); 60 | TEST_EQUAL(op1 + ninf, ninf); 61 | TEST_EQUAL(op1 + cl2, op3); 62 | TEST_EQUAL(op1 + op2, op3); 63 | 64 | } 65 | 66 | void test_rs_interval_continuous_boundary_subtraction() { 67 | 68 | static const B none = {0, BT::empty}; 69 | static const B ninf = {0, BT::minus_infinity}; 70 | static const B pinf = {0, BT::plus_infinity}; 71 | static const B cl1 = {1, BT::closed}; 72 | static const B op1 = {1, BT::open}; 73 | static const B cl2 = {2, BT::closed}; 74 | static const B op2 = {2, BT::open}; 75 | static const B cl3 = {3, BT::closed}; 76 | static const B op3 = {3, BT::open}; 77 | 78 | TEST_EQUAL(none - none, none); 79 | TEST_EQUAL(none - ninf, none); 80 | TEST_EQUAL(none - pinf, none); 81 | TEST_EQUAL(none - cl2, none); 82 | TEST_EQUAL(none - op2, none); 83 | TEST_EQUAL(ninf - none, none); 84 | TEST_EQUAL(ninf - pinf, ninf); 85 | TEST_EQUAL(ninf - cl2, ninf); 86 | TEST_EQUAL(pinf - none, none); 87 | TEST_EQUAL(pinf - ninf, pinf); 88 | TEST_EQUAL(pinf - cl2, pinf); 89 | TEST_EQUAL(pinf - op2, pinf); 90 | TEST_EQUAL(cl3 - none, none); 91 | TEST_EQUAL(cl3 - ninf, pinf); 92 | TEST_EQUAL(cl3 - pinf, ninf); 93 | TEST_EQUAL(cl3 - cl2, cl1); 94 | TEST_EQUAL(cl3 - op2, op1); 95 | TEST_EQUAL(op3 - none, none); 96 | TEST_EQUAL(op3 - pinf, ninf); 97 | TEST_EQUAL(op3 - cl2, op1); 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/test/continuous-boundary-basic-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/interval.hpp" 2 | #include "rs-interval/types.hpp" 3 | #include "test/unit-test.hpp" 4 | #include 5 | 6 | using namespace RS::Interval; 7 | using namespace RS::Interval::Detail; 8 | 9 | using Itv = Interval; 10 | using B = Boundary; 11 | using BT = BoundaryType; 12 | 13 | void test_rs_interval_continuous_boundary_formatting() { 14 | 15 | B b; 16 | 17 | TRY((b = {42, BT::empty})); TEST_EQUAL(std::format("{}", b), "{}"); 18 | TRY((b = {42, BT::minus_infinity})); TEST_EQUAL(std::format("{}", b), "-inf"); 19 | TRY((b = {42, BT::plus_infinity})); TEST_EQUAL(std::format("{}", b), "+inf"); 20 | TRY((b = {42, BT::closed})); TEST_EQUAL(std::format("{}", b), "42"); 21 | TRY((b = {42, BT::open})); TEST_EQUAL(std::format("{}", b), "(42)"); 22 | 23 | } 24 | 25 | void test_rs_interval_continuous_boundary_from_interval() { 26 | 27 | static const Itv none; 28 | static const Itv all = Itv::all(); 29 | static const Itv eq1(1); 30 | static const Itv ge1(1, 1, Bound::closed, Bound::unbound); 31 | static const Itv gt1(1, 1, Bound::open, Bound::unbound); 32 | static const Itv le1(1, 1, Bound::unbound, Bound::closed); 33 | static const Itv lt1(1, 1, Bound::unbound, Bound::open); 34 | static const Itv c12(1, 2, Bound::closed, Bound::closed); 35 | static const Itv o12(1, 2, Bound::open, Bound::open); 36 | static const Itv o1c2(1, 2, Bound::open, Bound::closed); 37 | static const Itv c1o2(1, 2, Bound::closed, Bound::open); 38 | 39 | TEST_EQUAL(std::format("{}", none), "{}"); 40 | TEST_EQUAL(std::format("{}", all), "*"); 41 | TEST_EQUAL(std::format("{}", eq1), "1"); 42 | TEST_EQUAL(std::format("{}", ge1), ">=1"); 43 | TEST_EQUAL(std::format("{}", gt1), ">1"); 44 | TEST_EQUAL(std::format("{}", le1), "<=1"); 45 | TEST_EQUAL(std::format("{}", lt1), "<1"); 46 | TEST_EQUAL(std::format("{}", c12), "[1,2]"); 47 | TEST_EQUAL(std::format("{}", o12), "(1,2)"); 48 | TEST_EQUAL(std::format("{}", o1c2), "(1,2]"); 49 | TEST_EQUAL(std::format("{}", c1o2), "[1,2)"); 50 | 51 | TEST_EQUAL(left_boundary_of(none), (B{0, BT::empty})); 52 | TEST_EQUAL(left_boundary_of(all), (B{0, BT::minus_infinity})); 53 | TEST_EQUAL(left_boundary_of(eq1), (B{1, BT::closed})); 54 | TEST_EQUAL(left_boundary_of(ge1), (B{1, BT::closed})); 55 | TEST_EQUAL(left_boundary_of(gt1), (B{1, BT::open})); 56 | TEST_EQUAL(left_boundary_of(le1), (B{0, BT::minus_infinity})); 57 | TEST_EQUAL(left_boundary_of(lt1), (B{0, BT::minus_infinity})); 58 | TEST_EQUAL(left_boundary_of(c12), (B{1, BT::closed})); 59 | TEST_EQUAL(left_boundary_of(o12), (B{1, BT::open})); 60 | TEST_EQUAL(left_boundary_of(o1c2), (B{1, BT::open})); 61 | TEST_EQUAL(left_boundary_of(c1o2), (B{1, BT::closed})); 62 | 63 | TEST_EQUAL(right_boundary_of(none), (B{0, BT::empty})); 64 | TEST_EQUAL(right_boundary_of(all), (B{0, BT::plus_infinity})); 65 | TEST_EQUAL(right_boundary_of(eq1), (B{1, BT::closed})); 66 | TEST_EQUAL(right_boundary_of(ge1), (B{0, BT::plus_infinity})); 67 | TEST_EQUAL(right_boundary_of(gt1), (B{0, BT::plus_infinity})); 68 | TEST_EQUAL(right_boundary_of(le1), (B{1, BT::closed})); 69 | TEST_EQUAL(right_boundary_of(lt1), (B{1, BT::open})); 70 | TEST_EQUAL(right_boundary_of(c12), (B{2, BT::closed})); 71 | TEST_EQUAL(right_boundary_of(o12), (B{2, BT::open})); 72 | TEST_EQUAL(right_boundary_of(o1c2), (B{2, BT::closed})); 73 | TEST_EQUAL(right_boundary_of(c1o2), (B{2, BT::open})); 74 | 75 | } 76 | 77 | void test_rs_interval_continuous_boundary_to_interval() { 78 | 79 | static const B none = {0, BT::empty}; 80 | static const B ninf = {0, BT::minus_infinity}; 81 | static const B pinf = {0, BT::plus_infinity}; 82 | static const B cl1 = {1, BT::closed}; 83 | static const B op1 = {1, BT::open}; 84 | static const B cl2 = {2, BT::closed}; 85 | static const B op2 = {2, BT::open}; 86 | 87 | TEST_EQUAL(interval_from_boundaries(none, none), Itv()); 88 | TEST_EQUAL(interval_from_boundaries(ninf, pinf), Itv::all()); 89 | TEST_EQUAL(interval_from_boundaries(ninf, cl1), Itv(1, 1, "<=")); 90 | TEST_EQUAL(interval_from_boundaries(ninf, op1), Itv(1, 1, "<")); 91 | TEST_EQUAL(interval_from_boundaries(cl1, pinf), Itv(1, 1, ">=")); 92 | TEST_EQUAL(interval_from_boundaries(op1, pinf), Itv(1, 1, ">")); 93 | TEST_EQUAL(interval_from_boundaries(cl1, cl1), Itv(1)); 94 | TEST_EQUAL(interval_from_boundaries(cl1, cl2), Itv(1, 2, "[]")); 95 | TEST_EQUAL(interval_from_boundaries(cl1, op2), Itv(1, 2, "[)")); 96 | TEST_EQUAL(interval_from_boundaries(op1, cl2), Itv(1, 2, "(]")); 97 | TEST_EQUAL(interval_from_boundaries(op1, op2), Itv(1, 2, "()")); 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/test/continuous-boundary-comparison-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/types.hpp" 2 | #include "test/unit-test.hpp" 3 | 4 | using namespace RS::Interval; 5 | using namespace RS::Interval::Detail; 6 | 7 | using B = Boundary; 8 | using BT = BoundaryType; 9 | 10 | void test_rs_interval_continuous_boundary_adjacency() { 11 | 12 | static const B none = {0, BT::empty}; 13 | static const B ninf = {0, BT::minus_infinity}; 14 | static const B pinf = {0, BT::plus_infinity}; 15 | static const B cl1 = {1, BT::closed}; 16 | static const B op1 = {1, BT::open}; 17 | static const B cl2 = {2, BT::closed}; 18 | static const B op2 = {2, BT::open}; 19 | 20 | TEST(! none.adjacent(none)); 21 | TEST(! none.adjacent(ninf)); 22 | TEST(! none.adjacent(pinf)); 23 | TEST(! none.adjacent(cl1)); 24 | TEST(! none.adjacent(op1)); 25 | TEST(! ninf.adjacent(none)); 26 | TEST(! ninf.adjacent(ninf)); 27 | TEST(! ninf.adjacent(pinf)); 28 | TEST(! ninf.adjacent(cl1)); 29 | TEST(! ninf.adjacent(op1)); 30 | TEST(! pinf.adjacent(none)); 31 | TEST(! pinf.adjacent(ninf)); 32 | TEST(! pinf.adjacent(pinf)); 33 | TEST(! pinf.adjacent(cl1)); 34 | TEST(! pinf.adjacent(op1)); 35 | TEST(! cl1.adjacent(none)); 36 | TEST(! cl1.adjacent(ninf)); 37 | TEST(! cl1.adjacent(pinf)); 38 | TEST(! cl1.adjacent(cl1)); 39 | TEST(cl1.adjacent(op1)); 40 | TEST(! cl1.adjacent(cl2)); 41 | TEST(! cl1.adjacent(op2)); 42 | TEST(! op1.adjacent(none)); 43 | TEST(! op1.adjacent(ninf)); 44 | TEST(! op1.adjacent(pinf)); 45 | TEST(op1.adjacent(cl1)); 46 | TEST(! op1.adjacent(op1)); 47 | TEST(! op1.adjacent(cl2)); 48 | TEST(! op1.adjacent(op2)); 49 | TEST(! cl2.adjacent(cl1)); 50 | TEST(! cl2.adjacent(op1)); 51 | TEST(! op2.adjacent(cl1)); 52 | TEST(! op2.adjacent(op1)); 53 | 54 | } 55 | 56 | void test_rs_interval_continuous_boundary_comparison() { 57 | 58 | static const B none = {0, BT::empty}; 59 | static const B ninf = {0, BT::minus_infinity}; 60 | static const B pinf = {0, BT::plus_infinity}; 61 | static const B cl1 = {1, BT::closed}; 62 | static const B op1 = {1, BT::open}; 63 | static const B cl2 = {2, BT::closed}; 64 | static const B op2 = {2, BT::open}; 65 | 66 | TEST(! none.compare_ll(none)); 67 | TEST(none.compare_ll(ninf)); 68 | TEST(none.compare_ll(pinf)); 69 | TEST(none.compare_ll(cl1)); 70 | TEST(none.compare_ll(op1)); 71 | TEST(! ninf.compare_ll(none)); 72 | TEST(! ninf.compare_ll(ninf)); 73 | TEST(ninf.compare_ll(pinf)); 74 | TEST(ninf.compare_ll(cl1)); 75 | TEST(ninf.compare_ll(op1)); 76 | TEST(! pinf.compare_ll(none)); 77 | TEST(! pinf.compare_ll(ninf)); 78 | TEST(! pinf.compare_ll(pinf)); 79 | TEST(! pinf.compare_ll(cl1)); 80 | TEST(! pinf.compare_ll(op1)); 81 | TEST(! cl1.compare_ll(none)); 82 | TEST(! cl1.compare_ll(ninf)); 83 | TEST(cl1.compare_ll(pinf)); 84 | TEST(! cl1.compare_ll(cl1)); 85 | TEST(cl1.compare_ll(op1)); 86 | TEST(cl1.compare_ll(cl2)); 87 | TEST(cl1.compare_ll(op2)); 88 | TEST(! op1.compare_ll(none)); 89 | TEST(! op1.compare_ll(ninf)); 90 | TEST(op1.compare_ll(pinf)); 91 | TEST(! op1.compare_ll(cl1)); 92 | TEST(! op1.compare_ll(op1)); 93 | TEST(op1.compare_ll(cl2)); 94 | TEST(op1.compare_ll(op2)); 95 | TEST(! cl2.compare_ll(cl1)); 96 | TEST(! cl2.compare_ll(op1)); 97 | TEST(! op2.compare_ll(cl1)); 98 | TEST(! op2.compare_ll(op1)); 99 | 100 | TEST(! none.compare_rr(none)); 101 | TEST(none.compare_rr(ninf)); 102 | TEST(none.compare_rr(pinf)); 103 | TEST(none.compare_rr(cl1)); 104 | TEST(none.compare_rr(op1)); 105 | TEST(! ninf.compare_rr(none)); 106 | TEST(! ninf.compare_rr(ninf)); 107 | TEST(ninf.compare_rr(pinf)); 108 | TEST(ninf.compare_rr(cl1)); 109 | TEST(ninf.compare_rr(op1)); 110 | TEST(! pinf.compare_rr(none)); 111 | TEST(! pinf.compare_rr(ninf)); 112 | TEST(! pinf.compare_rr(pinf)); 113 | TEST(! pinf.compare_rr(cl1)); 114 | TEST(! pinf.compare_rr(op1)); 115 | TEST(! cl1.compare_rr(none)); 116 | TEST(! cl1.compare_rr(ninf)); 117 | TEST(cl1.compare_rr(pinf)); 118 | TEST(! cl1.compare_rr(cl1)); 119 | TEST(! cl1.compare_rr(op1)); 120 | TEST(cl1.compare_rr(cl2)); 121 | TEST(cl1.compare_rr(op2)); 122 | TEST(! op1.compare_rr(none)); 123 | TEST(! op1.compare_rr(ninf)); 124 | TEST(op1.compare_rr(pinf)); 125 | TEST(op1.compare_rr(cl1)); 126 | TEST(! op1.compare_rr(op1)); 127 | TEST(op1.compare_rr(cl2)); 128 | TEST(op1.compare_rr(op2)); 129 | TEST(! cl2.compare_rr(cl1)); 130 | TEST(! cl2.compare_rr(op1)); 131 | TEST(! op2.compare_rr(cl1)); 132 | TEST(! op2.compare_rr(op1)); 133 | 134 | TEST(! none.compare_lr(none)); 135 | TEST(none.compare_lr(ninf)); 136 | TEST(none.compare_lr(pinf)); 137 | TEST(none.compare_lr(cl1)); 138 | TEST(none.compare_lr(op1)); 139 | TEST(! ninf.compare_lr(none)); 140 | TEST(! ninf.compare_lr(ninf)); 141 | TEST(ninf.compare_lr(pinf)); 142 | TEST(ninf.compare_lr(cl1)); 143 | TEST(ninf.compare_lr(op1)); 144 | TEST(! pinf.compare_lr(none)); 145 | TEST(! pinf.compare_lr(ninf)); 146 | TEST(! pinf.compare_lr(pinf)); 147 | TEST(! pinf.compare_lr(cl1)); 148 | TEST(! pinf.compare_lr(op1)); 149 | TEST(! cl1.compare_lr(none)); 150 | TEST(! cl1.compare_lr(ninf)); 151 | TEST(cl1.compare_lr(pinf)); 152 | TEST(! cl1.compare_lr(cl1)); 153 | TEST(! cl1.compare_lr(op1)); 154 | TEST(cl1.compare_lr(cl2)); 155 | TEST(cl1.compare_lr(op2)); 156 | TEST(! op1.compare_lr(none)); 157 | TEST(! op1.compare_lr(ninf)); 158 | TEST(op1.compare_lr(pinf)); 159 | TEST(! op1.compare_lr(cl1)); 160 | TEST(! op1.compare_lr(op1)); 161 | TEST(op1.compare_lr(cl2)); 162 | TEST(op1.compare_lr(op2)); 163 | TEST(! cl2.compare_lr(cl1)); 164 | TEST(! cl2.compare_lr(op1)); 165 | TEST(! op2.compare_lr(cl1)); 166 | TEST(! op2.compare_lr(op1)); 167 | 168 | TEST(! none.compare_rl(none)); 169 | TEST(none.compare_rl(ninf)); 170 | TEST(none.compare_rl(pinf)); 171 | TEST(none.compare_rl(cl1)); 172 | TEST(none.compare_rl(op1)); 173 | TEST(! ninf.compare_rl(none)); 174 | TEST(! ninf.compare_rl(ninf)); 175 | TEST(ninf.compare_rl(pinf)); 176 | TEST(ninf.compare_rl(cl1)); 177 | TEST(ninf.compare_rl(op1)); 178 | TEST(! pinf.compare_rl(none)); 179 | TEST(! pinf.compare_rl(ninf)); 180 | TEST(! pinf.compare_rl(pinf)); 181 | TEST(! pinf.compare_rl(cl1)); 182 | TEST(! pinf.compare_rl(op1)); 183 | TEST(! cl1.compare_rl(none)); 184 | TEST(! cl1.compare_rl(ninf)); 185 | TEST(cl1.compare_rl(pinf)); 186 | TEST(! cl1.compare_rl(cl1)); 187 | TEST(cl1.compare_rl(op1)); 188 | TEST(cl1.compare_rl(cl2)); 189 | TEST(cl1.compare_rl(op2)); 190 | TEST(! op1.compare_rl(none)); 191 | TEST(! op1.compare_rl(ninf)); 192 | TEST(op1.compare_rl(pinf)); 193 | TEST(op1.compare_rl(cl1)); 194 | TEST(op1.compare_rl(op1)); 195 | TEST(op1.compare_rl(cl2)); 196 | TEST(op1.compare_rl(op2)); 197 | TEST(! cl2.compare_rl(cl1)); 198 | TEST(! cl2.compare_rl(op1)); 199 | TEST(! op2.compare_rl(cl1)); 200 | TEST(! op2.compare_rl(op1)); 201 | 202 | } 203 | -------------------------------------------------------------------------------- /src/test/continuous-boundary-multiplication-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/types.hpp" 2 | #include "test/unit-test.hpp" 3 | 4 | using namespace RS::Interval; 5 | using namespace RS::Interval::Detail; 6 | 7 | using B = Boundary; 8 | using BT = BoundaryType; 9 | 10 | void test_rs_interval_continuous_boundary_multiplication() { 11 | 12 | static const B none = {0, BT::empty}; 13 | static const B ninf = {0, BT::minus_infinity}; 14 | static const B pinf = {0, BT::plus_infinity}; 15 | static const B cl0 = {0, BT::closed}; 16 | static const B op0 = {0, BT::open}; 17 | static const B cl2 = {2, BT::closed}; 18 | static const B op2 = {2, BT::open}; 19 | static const B cl3 = {3, BT::closed}; 20 | static const B op3 = {3, BT::open}; 21 | static const B cl6 = {6, BT::closed}; 22 | static const B op6 = {6, BT::open}; 23 | static const B cl_2 = {-2, BT::closed}; 24 | static const B op_2 = {-2, BT::open}; 25 | static const B cl_3 = {-3, BT::closed}; 26 | static const B op_3 = {-3, BT::open}; 27 | static const B cl_6 = {-6, BT::closed}; 28 | static const B op_6 = {-6, BT::open}; 29 | 30 | TEST_EQUAL(none * none, none); 31 | TEST_EQUAL(none * ninf, none); 32 | TEST_EQUAL(none * pinf, none); 33 | TEST_EQUAL(none * cl_3, none); 34 | TEST_EQUAL(none * cl3, none); 35 | TEST_EQUAL(none * cl0, none); 36 | TEST_EQUAL(none * op_3, none); 37 | TEST_EQUAL(none * op3, none); 38 | TEST_EQUAL(none * op0, none); 39 | TEST_EQUAL(ninf * none, none); 40 | TEST_EQUAL(ninf * ninf, pinf); 41 | TEST_EQUAL(ninf * pinf, ninf); 42 | TEST_EQUAL(ninf * cl_3, pinf); 43 | TEST_EQUAL(ninf * cl3, ninf); 44 | TEST_EQUAL(ninf * cl0, cl0); 45 | TEST_EQUAL(ninf * op_3, pinf); 46 | TEST_EQUAL(ninf * op3, ninf); 47 | TEST_EQUAL(ninf * op0, op0); 48 | TEST_EQUAL(pinf * none, none); 49 | TEST_EQUAL(pinf * ninf, ninf); 50 | TEST_EQUAL(pinf * pinf, pinf); 51 | TEST_EQUAL(pinf * cl_3, ninf); 52 | TEST_EQUAL(pinf * cl3, pinf); 53 | TEST_EQUAL(pinf * cl0, cl0); 54 | TEST_EQUAL(pinf * op_3, ninf); 55 | TEST_EQUAL(pinf * op3, pinf); 56 | TEST_EQUAL(pinf * op0, op0); 57 | TEST_EQUAL(cl_2 * none, none); 58 | TEST_EQUAL(cl_2 * ninf, pinf); 59 | TEST_EQUAL(cl_2 * pinf, ninf); 60 | TEST_EQUAL(cl_2 * cl_3, cl6); 61 | TEST_EQUAL(cl_2 * cl3, cl_6); 62 | TEST_EQUAL(cl_2 * cl0, cl0); 63 | TEST_EQUAL(cl_2 * op_3, op6); 64 | TEST_EQUAL(cl_2 * op3, op_6); 65 | TEST_EQUAL(cl_2 * op0, op0); 66 | TEST_EQUAL(cl2 * none, none); 67 | TEST_EQUAL(cl2 * ninf, ninf); 68 | TEST_EQUAL(cl2 * pinf, pinf); 69 | TEST_EQUAL(cl2 * cl_3, cl_6); 70 | TEST_EQUAL(cl2 * cl3, cl6); 71 | TEST_EQUAL(cl2 * cl0, cl0); 72 | TEST_EQUAL(cl2 * op_3, op_6); 73 | TEST_EQUAL(cl2 * op3, op6); 74 | TEST_EQUAL(cl2 * op0, op0); 75 | TEST_EQUAL(cl0 * none, none); 76 | TEST_EQUAL(cl0 * ninf, cl0); 77 | TEST_EQUAL(cl0 * pinf, cl0); 78 | TEST_EQUAL(cl0 * cl_3, cl0); 79 | TEST_EQUAL(cl0 * cl3, cl0); 80 | TEST_EQUAL(cl0 * cl0, cl0); 81 | TEST_EQUAL(cl0 * op_3, cl0); 82 | TEST_EQUAL(cl0 * op3, cl0); 83 | TEST_EQUAL(cl0 * op0, cl0); 84 | TEST_EQUAL(op_2 * none, none); 85 | TEST_EQUAL(op_2 * ninf, pinf); 86 | TEST_EQUAL(op_2 * pinf, ninf); 87 | TEST_EQUAL(op_2 * cl_3, op6); 88 | TEST_EQUAL(op_2 * cl3, op_6); 89 | TEST_EQUAL(op_2 * cl0, cl0); 90 | TEST_EQUAL(op_2 * op_3, op6); 91 | TEST_EQUAL(op_2 * op3, op_6); 92 | TEST_EQUAL(op_2 * op0, op0); 93 | TEST_EQUAL(op2 * none, none); 94 | TEST_EQUAL(op2 * ninf, ninf); 95 | TEST_EQUAL(op2 * pinf, pinf); 96 | TEST_EQUAL(op2 * cl_3, op_6); 97 | TEST_EQUAL(op2 * cl3, op6); 98 | TEST_EQUAL(op2 * cl0, cl0); 99 | TEST_EQUAL(op2 * op_3, op_6); 100 | TEST_EQUAL(op2 * op3, op6); 101 | TEST_EQUAL(op2 * op0, op0); 102 | TEST_EQUAL(op0 * none, none); 103 | TEST_EQUAL(op0 * ninf, op0); 104 | TEST_EQUAL(op0 * pinf, op0); 105 | TEST_EQUAL(op0 * cl_3, op0); 106 | TEST_EQUAL(op0 * cl3, op0); 107 | TEST_EQUAL(op0 * cl0, cl0); 108 | TEST_EQUAL(op0 * op_3, op0); 109 | TEST_EQUAL(op0 * op3, op0); 110 | TEST_EQUAL(op0 * op0, op0); 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/test/continuous-map-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/interval.hpp" 2 | #include "rs-interval/map.hpp" 3 | #include "rs-interval/set.hpp" 4 | #include "rs-interval/types.hpp" 5 | #include "test/unit-test.hpp" 6 | #include 7 | #include 8 | 9 | using namespace RS::Interval; 10 | 11 | using Itv = Interval; 12 | using Map = IntervalMap; 13 | 14 | void test_rs_interval_continuous_map() { 15 | 16 | Map map; 17 | Map::iterator it; 18 | 19 | TEST(map.empty()); 20 | TEST_EQUAL(map.size(), 0u); 21 | TEST_EQUAL(map.default_value(), ""); 22 | TEST_EQUAL(map[42], ""); 23 | TEST_EQUAL(std::format("{}", map), "{}"); 24 | 25 | TRY(map.insert(Itv(2,2,"<="), "alpha")); 26 | TRY(map.insert(Itv(3,7,"()"), "bravo")); 27 | TRY(map.insert(Itv(8,8,">="), "charlie")); 28 | TEST_EQUAL(map.size(), 3u); 29 | TEST_EQUAL(map[1], "alpha"); 30 | TEST_EQUAL(map[2], "alpha"); 31 | TEST_EQUAL(map[3], ""); 32 | TEST_EQUAL(map[4], "bravo"); 33 | TEST_EQUAL(map[5], "bravo"); 34 | TEST_EQUAL(map[6], "bravo"); 35 | TEST_EQUAL(map[7], ""); 36 | TEST_EQUAL(map[8], "charlie"); 37 | TEST_EQUAL(map[9], "charlie"); 38 | TRY(map.default_value("nil")); 39 | TEST_EQUAL(map[1], "alpha"); 40 | TEST_EQUAL(map[2], "alpha"); 41 | TEST_EQUAL(map[3], "nil"); 42 | TEST_EQUAL(map[4], "bravo"); 43 | TEST_EQUAL(map[5], "bravo"); 44 | TEST_EQUAL(map[6], "bravo"); 45 | TEST_EQUAL(map[7], "nil"); 46 | TEST_EQUAL(map[8], "charlie"); 47 | TEST_EQUAL(map[9], "charlie"); 48 | TEST_EQUAL(std::format("{}", map), "{<=2:alpha,(3,7):bravo,>=8:charlie}"); 49 | 50 | TEST(map.contains(1)); 51 | TEST(map.contains(2)); 52 | TEST(! map.contains(3)); 53 | TEST(map.contains(4)); 54 | TEST(map.contains(5)); 55 | TEST(map.contains(6)); 56 | TEST(! map.contains(7)); 57 | TEST(map.contains(8)); 58 | TEST(map.contains(9)); 59 | 60 | TRY(it = map.find(1)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "alpha"); 61 | TRY(it = map.find(2)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "alpha"); 62 | TRY(it = map.find(3)); TEST(it == map.end()); 63 | TRY(it = map.find(4)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 64 | TRY(it = map.find(5)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 65 | TRY(it = map.find(6)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 66 | TRY(it = map.find(7)); TEST(it == map.end()); 67 | TRY(it = map.find(8)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 68 | TRY(it = map.find(9)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 69 | 70 | TRY(it = map.lower_bound(1)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "alpha"); 71 | TRY(it = map.lower_bound(2)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "alpha"); 72 | TRY(it = map.lower_bound(3)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 73 | TRY(it = map.lower_bound(4)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 74 | TRY(it = map.lower_bound(5)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 75 | TRY(it = map.lower_bound(6)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 76 | TRY(it = map.lower_bound(7)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 77 | TRY(it = map.lower_bound(8)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 78 | TRY(it = map.lower_bound(9)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 79 | 80 | TRY(it = map.upper_bound(1)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 81 | TRY(it = map.upper_bound(2)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 82 | TRY(it = map.upper_bound(3)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 83 | TRY(it = map.upper_bound(4)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 84 | TRY(it = map.upper_bound(5)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 85 | TRY(it = map.upper_bound(6)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 86 | TRY(it = map.upper_bound(7)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 87 | TRY(it = map.upper_bound(8)); TEST(it == map.end()); 88 | TRY(it = map.upper_bound(9)); TEST(it == map.end()); 89 | 90 | TRY(map.clear()); 91 | TEST(map.empty()); 92 | TRY((map = { 93 | {Itv(20,20,"<="), "alpha"}, 94 | {Itv(30,70,"()"), "bravo"}, 95 | {Itv(80,80,">="), "charlie"}, 96 | })); 97 | TEST_EQUAL(map.size(), 3u); 98 | TEST_EQUAL(std::format("{}", map), "{<=20:alpha,(30,70):bravo,>=80:charlie}"); 99 | 100 | TRY(map.clear()); 101 | TRY(map.default_value("nil")); 102 | TRY(map.insert(Itv(10,10,"<="), "alpha")); 103 | TRY(map.insert(Itv(1,5,"[]"), "bravo")); 104 | TRY(map.insert(Itv(5,6,"[]"), "charlie")); 105 | TRY(map.insert(Itv(1,4,"()"), "delta")); 106 | TRY(map.insert(Itv(6,8,"[]"), "charlie")); 107 | TEST_EQUAL(map.size(), 6u); 108 | TEST_EQUAL(std::format("{}", map), 109 | "{<1:alpha," 110 | "1:bravo," 111 | "(1,4):delta," 112 | "[4,5):bravo," 113 | "[5,8]:charlie," 114 | "(8,10]:alpha}"); 115 | TEST_EQUAL(map[0], "alpha"); 116 | TEST_EQUAL(map[1], "bravo"); 117 | TEST_EQUAL(map[2], "delta"); 118 | TEST_EQUAL(map[3], "delta"); 119 | TEST_EQUAL(map[4], "bravo"); 120 | TEST_EQUAL(map[5], "charlie"); 121 | TEST_EQUAL(map[6], "charlie"); 122 | TEST_EQUAL(map[7], "charlie"); 123 | TEST_EQUAL(map[8], "charlie"); 124 | TEST_EQUAL(map[9], "alpha"); 125 | TEST_EQUAL(map[10], "alpha"); 126 | TEST_EQUAL(map[11], "nil"); 127 | TEST_EQUAL(map[12], "nil"); 128 | 129 | TRY(map.erase(Itv(1,2,"[]"))); 130 | TRY(map.erase(Itv(6,8,"()"))); 131 | TRY(map.erase(Itv(10,10,">="))); 132 | TEST_EQUAL(map.size(), 6u); 133 | TEST_EQUAL(std::format("{}", map), 134 | "{<1:alpha," 135 | "(2,4):delta," 136 | "[4,5):bravo," 137 | "[5,6]:charlie," 138 | "8:charlie," 139 | "(8,10):alpha}"); 140 | TEST_EQUAL(map[0], "alpha"); 141 | TEST_EQUAL(map[1], "nil"); 142 | TEST_EQUAL(map[2], "nil"); 143 | TEST_EQUAL(map[3], "delta"); 144 | TEST_EQUAL(map[4], "bravo"); 145 | TEST_EQUAL(map[5], "charlie"); 146 | TEST_EQUAL(map[6], "charlie"); 147 | TEST_EQUAL(map[7], "nil"); 148 | TEST_EQUAL(map[8], "charlie"); 149 | TEST_EQUAL(map[9], "alpha"); 150 | TEST_EQUAL(map[10], "nil"); 151 | TEST_EQUAL(map[11], "nil"); 152 | TEST_EQUAL(map[12], "nil"); 153 | 154 | } 155 | -------------------------------------------------------------------------------- /src/test/continuous-set-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/interval.hpp" 2 | #include "rs-interval/set.hpp" 3 | #include "rs-interval/types.hpp" 4 | #include "test/unit-test.hpp" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace RS::Interval; 12 | 13 | using Itv = Interval; 14 | using Set = IntervalSet; 15 | 16 | void test_rs_interval_continuous_set_construct_insert_erase() { 17 | 18 | Set set, com; 19 | Set::iterator it; 20 | Itv in; 21 | std::string str; 22 | 23 | TEST(set.empty()); 24 | TRY(set = 42); 25 | TEST_EQUAL(set.size(), 1u); 26 | TRY(it = set.begin()); 27 | TEST_EQUAL(std::format("{}", *it), "42"); 28 | TRY(++it); 29 | TEST(it == set.end()); 30 | TRY((in = {5,10})); 31 | TRY(set = in); 32 | TEST_EQUAL(set.size(), 1u); 33 | TRY(it = set.begin()); 34 | TEST_EQUAL(std::format("{}", *it), "[5,10]"); 35 | TRY(++it); 36 | TEST(it == set.end()); 37 | TRY((set = {{5,10},{15,20},{25,30}})); 38 | TRY(it = set.begin()); 39 | TEST_EQUAL(std::format("{}", *it), "[5,10]"); 40 | TRY(++it); 41 | TEST_EQUAL(std::format("{}", *it), "[15,20]"); 42 | TRY(++it); 43 | TEST_EQUAL(std::format("{}", *it), "[25,30]"); 44 | TRY(++it); 45 | TEST(it == set.end()); 46 | 47 | TRY((set = {})); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{}"); 48 | TRY((set = {{1,2,"()"}})); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{(1,2)}"); 49 | TRY((set = {{1,2,"()"},{3,4,"()"},{5,6,"()"}})); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{(1,2),(3,4),(5,6)}"); 50 | TRY((set = {{1,2,"[]"},{3,4,"[]"},{5,6,"[]"}})); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[1,2],[3,4],[5,6]}"); 51 | 52 | TRY((set = {{1,2,"[)"},{3,4,"(]"},{5,6,"()"}})); 53 | TRY(com = set.complement()); 54 | TRY(str = std::format("{}", set)); 55 | TEST_EQUAL(str, "{[1,2),(3,4],(5,6)}"); 56 | TRY(str = std::format("{}", com)); 57 | TEST_EQUAL(str, "{<1,[2,3],(4,5],>=6}"); 58 | 59 | TEST(! set.contains(0.5)); TEST(com.contains(0.5)); 60 | TEST(set.contains(1.0)); TEST(! com.contains(1.0)); 61 | TEST(set.contains(1.5)); TEST(! com.contains(1.5)); 62 | TEST(! set.contains(2.0)); TEST(com.contains(2.0)); 63 | TEST(! set.contains(2.5)); TEST(com.contains(2.5)); 64 | TEST(! set.contains(3.0)); TEST(com.contains(3.0)); 65 | TEST(set.contains(3.5)); TEST(! com.contains(3.5)); 66 | TEST(set.contains(4.0)); TEST(! com.contains(4.0)); 67 | TEST(! set.contains(4.5)); TEST(com.contains(4.5)); 68 | TEST(! set.contains(5.0)); TEST(com.contains(5.0)); 69 | TEST(set.contains(5.5)); TEST(! com.contains(5.5)); 70 | TEST(! set.contains(6.0)); TEST(com.contains(6.0)); 71 | TEST(! set.contains(6.5)); TEST(com.contains(6.5)); 72 | TEST(! set.contains(7.0)); TEST(com.contains(7.0)); 73 | 74 | TRY(set.clear()); 75 | TEST(set.empty()); 76 | 77 | TRY((set.insert({10,20}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[10,20]}"); 78 | TRY((set.insert({20,30,"()"}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[10,30)}"); 79 | TRY((set.erase({5,10,"()"}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[10,30)}"); 80 | TRY((set.erase({5,10}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{(10,30)}"); 81 | TRY((set.erase({12,14}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{(10,12),(14,30)}"); 82 | TRY((set.erase({16,18,"()"}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{(10,12),(14,16],[18,30)}"); 83 | TRY((set.insert({9,11}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[9,12),(14,16],[18,30)}"); 84 | TRY((set.insert({29,31,"()"}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[9,12),(14,16],[18,31)}"); 85 | 86 | } 87 | 88 | void test_rs_interval_continuous_set_formatting() { 89 | 90 | Set set; 91 | std::string str; 92 | 93 | TRY((set = {})); TRY(str = std::format("{:.2f}", set)); TEST_EQUAL(str, "{}"); 94 | TRY((set = {{1,2,"()"}})); TRY(str = std::format("{:.2f}", set)); TEST_EQUAL(str, "{(1.00,2.00)}"); 95 | TRY((set = {{1,2,"()"},{3,4,"()"},{5,6,"()"}})); TRY(str = std::format("{:.2f}", set)); TEST_EQUAL(str, "{(1.00,2.00),(3.00,4.00),(5.00,6.00)}"); 96 | TRY((set = {{1,2,"[]"},{3,4,"[]"},{5,6,"[]"}})); TRY(str = std::format("{:.2f}", set)); TEST_EQUAL(str, "{[1.00,2.00],[3.00,4.00],[5.00,6.00]}"); 97 | 98 | } 99 | 100 | void test_rs_interval_continuous_set_operations() { 101 | 102 | using random_int = std::uniform_int_distribution; 103 | 104 | Set set[2], i_set, u_set, d_set, sd_set; 105 | std::vector vec[2]; 106 | Set::iterator it; 107 | Itv in; 108 | std::minstd_rand rng(42); 109 | 110 | static constexpr int iterations = 1000; 111 | static constexpr int max_size = 10; 112 | static constexpr int max_value = 50; 113 | 114 | for (int i = 0; i < iterations; ++i) { 115 | 116 | for (int j = 0; j < 2; ++j) { 117 | 118 | TRY(set[j].clear()); 119 | vec[j].clear(); 120 | int size = random_int(1, max_size)(rng); 121 | 122 | for (int k = 0; k < size; ++k) { 123 | 124 | auto a = double(random_int(1, max_value)(rng)); 125 | auto b = double(random_int(1, max_value)(rng)); 126 | auto l = Bound(random_int(0, 3)(rng)); 127 | auto r = Bound(random_int(0, 3)(rng)); 128 | 129 | if ((l == Bound::empty) == (r == Bound::empty)) { 130 | TRY(in = Itv(a, b, l, r)); 131 | TRY(set[j].insert(in)); 132 | vec[j].push_back(in); 133 | } 134 | 135 | } 136 | 137 | } 138 | 139 | TRY(i_set = set_intersection(set[0], set[1])); 140 | TRY(u_set = set_union(set[0], set[1])); 141 | TRY(d_set = set_difference(set[0], set[1])); 142 | TRY(sd_set = set_symmetric_difference(set[0], set[1])); 143 | 144 | for (int y = 0; y <= max_value + 1; ++y) { 145 | 146 | auto x = double(y); 147 | bool member[2]; 148 | 149 | for (int j = 0; j < 2; ++j) { 150 | member[j] = false; 151 | for (const auto& item: vec[j]) { 152 | member[j] |= item(x); 153 | } 154 | } 155 | 156 | auto i_expect = member[0] && member[1]; 157 | auto u_expect = member[0] || member[1]; 158 | auto d_expect = member[0] && ! member[1]; 159 | auto sd_expect = member[0] != member[1]; 160 | auto i_test = false; 161 | auto u_test = false; 162 | auto d_test = false; 163 | auto sd_test = false; 164 | 165 | TRY(i_test = i_set[x]); 166 | TRY(u_test = u_set[x]); 167 | TRY(d_test = d_set[x]); 168 | TRY(sd_test = sd_set[x]); 169 | TEST_EQUAL(i_test, i_expect); 170 | TEST_EQUAL(u_test, u_expect); 171 | TEST_EQUAL(d_test, d_expect); 172 | TEST_EQUAL(sd_test, sd_expect); 173 | 174 | if ((i_test != i_expect) || (u_test != u_expect) || (d_test != d_expect) || (sd_test != sd_expect)) { 175 | for (int j = 0; j < 2; ++j) { 176 | std::println("... {} => {}", vec[j], set[j]); 177 | } 178 | std::println("... x={}", x); 179 | } 180 | 181 | } 182 | 183 | TRY(i_set = set[0]); 184 | TRY(u_set = set[0]); 185 | TRY(d_set = set[0]); 186 | TRY(sd_set = set[0]); 187 | TRY(i_set.apply_intersection(set[1])); 188 | TRY(u_set.apply_union(set[1])); 189 | TRY(d_set.apply_difference(set[1])); 190 | TRY(sd_set.apply_symmetric_difference(set[1])); 191 | 192 | for (int y = 0; y <= max_value + 1; ++y) { 193 | 194 | auto x = double(y); 195 | bool member[2]; 196 | 197 | for (int j = 0; j < 2; ++j) { 198 | member[j] = false; 199 | for (const auto& item: vec[j]) { 200 | member[j] |= item(x); 201 | } 202 | } 203 | 204 | auto i_expect = member[0] && member[1]; 205 | auto u_expect = member[0] || member[1]; 206 | auto d_expect = member[0] && ! member[1]; 207 | auto sd_expect = member[0] != member[1]; 208 | auto i_test = false; 209 | auto u_test = false; 210 | auto d_test = false; 211 | auto sd_test = false; 212 | 213 | TRY(i_test = i_set[x]); 214 | TRY(u_test = u_set[x]); 215 | TRY(d_test = d_set[x]); 216 | TRY(sd_test = sd_set[x]); 217 | TEST_EQUAL(i_test, i_expect); 218 | TEST_EQUAL(u_test, u_expect); 219 | TEST_EQUAL(d_test, d_expect); 220 | TEST_EQUAL(sd_test, sd_expect); 221 | 222 | if ((i_test != i_expect) || (u_test != u_expect) || (d_test != d_expect) || (sd_test != sd_expect)) { 223 | for (int j = 0; j < 2; ++j) { 224 | std::println("... {} => {}", vec[j], set[j]); 225 | } 226 | std::println("... x={}", x); 227 | } 228 | 229 | } 230 | 231 | } 232 | 233 | } 234 | -------------------------------------------------------------------------------- /src/test/integral-arithmetic-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/interval.hpp" 2 | #include "rs-interval/types.hpp" 3 | #include "test/unit-test.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace RS::Interval; 10 | 11 | using Itv = Interval; 12 | 13 | void test_rs_interval_integral_contains_zero() { 14 | 15 | using namespace RS::Interval::Detail; 16 | 17 | Itv in; 18 | 19 | TRY(in = Itv()); TEST(! contains_zero(in)); 20 | TRY(in = Itv::all()); TEST(contains_zero(in)); 21 | TRY(in = Itv(-2)); TEST(! contains_zero(in)); 22 | TRY(in = Itv(-1)); TEST(! contains_zero(in)); 23 | TRY(in = Itv(0)); TEST(contains_zero(in)); 24 | TRY(in = Itv(1)); TEST(! contains_zero(in)); 25 | TRY(in = Itv(2)); TEST(! contains_zero(in)); 26 | TRY(in = Itv(-2,-2,"<=")); TEST(! contains_zero(in)); 27 | TRY(in = Itv(-1,-1,"<=")); TEST(! contains_zero(in)); 28 | TRY(in = Itv(0,0,"<=")); TEST(contains_zero(in)); 29 | TRY(in = Itv(1,1,"<=")); TEST(contains_zero(in)); 30 | TRY(in = Itv(2,2,"<=")); TEST(contains_zero(in)); 31 | TRY(in = Itv(-2,-2,"<")); TEST(! contains_zero(in)); 32 | TRY(in = Itv(-1,-1,"<")); TEST(! contains_zero(in)); 33 | TRY(in = Itv(0,0,"<")); TEST(! contains_zero(in)); 34 | TRY(in = Itv(1,1,"<")); TEST(contains_zero(in)); 35 | TRY(in = Itv(2,2,"<")); TEST(contains_zero(in)); 36 | TRY(in = Itv(-2,-2,">=")); TEST(contains_zero(in)); 37 | TRY(in = Itv(-1,-1,">=")); TEST(contains_zero(in)); 38 | TRY(in = Itv(0,0,">=")); TEST(contains_zero(in)); 39 | TRY(in = Itv(1,1,">=")); TEST(! contains_zero(in)); 40 | TRY(in = Itv(2,2,">=")); TEST(! contains_zero(in)); 41 | TRY(in = Itv(-2,-2,">")); TEST(contains_zero(in)); 42 | TRY(in = Itv(-1,-1,">")); TEST(contains_zero(in)); 43 | TRY(in = Itv(0,0,">")); TEST(! contains_zero(in)); 44 | TRY(in = Itv(1,1,">")); TEST(! contains_zero(in)); 45 | TRY(in = Itv(2,2,">")); TEST(! contains_zero(in)); 46 | TRY(in = Itv(-2,-1,"[]")); TEST(! contains_zero(in)); 47 | TRY(in = Itv(-2,-1,"()")); TEST(! contains_zero(in)); 48 | TRY(in = Itv(-1,0,"[]")); TEST(contains_zero(in)); 49 | TRY(in = Itv(-1,0,"()")); TEST(! contains_zero(in)); 50 | TRY(in = Itv(-1,1,"[]")); TEST(contains_zero(in)); 51 | TRY(in = Itv(-1,1,"()")); TEST(contains_zero(in)); 52 | TRY(in = Itv(0,1,"[]")); TEST(contains_zero(in)); 53 | TRY(in = Itv(0,1,"()")); TEST(! contains_zero(in)); 54 | TRY(in = Itv(1,2,"[]")); TEST(! contains_zero(in)); 55 | TRY(in = Itv(1,2,"()")); TEST(! contains_zero(in)); 56 | 57 | } 58 | 59 | void test_rs_interval_integral_interval_arithmetic() { 60 | 61 | Itv in; 62 | std::string str; 63 | 64 | TRY(in = + Itv()); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "{}"); 65 | TRY(in = + Itv::all()); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "*"); 66 | TRY(in = + Itv(1)); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "1"); 67 | TRY(in = - Itv()); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "{}"); 68 | TRY(in = - Itv::all()); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "*"); 69 | TRY(in = - Itv(1)); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "-1"); 70 | TRY(in = - Itv(1,5,"[]")); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "[-5,-1]"); 71 | TRY(in = - Itv(1,5,"()")); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "[-4,-2]"); 72 | TRY(in = - Itv(1,5,"[)")); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "[-4,-1]"); 73 | TRY(in = - Itv(1,5,"(]")); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "[-5,-2]"); 74 | TRY(in = - Itv(0,5,"<")); TRY(str = std::format("{}", in)); TEST_EQUAL(str, ">=-4"); 75 | TRY(in = - Itv(0,5,"<=")); TRY(str = std::format("{}", in)); TEST_EQUAL(str, ">=-5"); 76 | TRY(in = - Itv(5,0,">")); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "<=-6"); 77 | TRY(in = - Itv(5,0,">=")); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "<=-5"); 78 | 79 | TRY(in = Itv() + 42); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "{}"); 80 | TRY(in = Itv::all() + 42); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "*"); 81 | TRY(in = Itv(5) + 42); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "47"); 82 | TRY(in = Itv(5,10,"[]") + 42); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "[47,52]"); 83 | TRY(in = Itv(5,10,"()") + 42); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "[48,51]"); 84 | TRY(in = Itv(5,10,"[)") + 42); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "[47,51]"); 85 | TRY(in = Itv(5,10,"(]") + 42); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "[48,52]"); 86 | TRY(in = Itv(5,10,"<") + 42); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "<=51"); 87 | TRY(in = Itv(5,10,"<=") + 42); TRY(str = std::format("{}", in)); TEST_EQUAL(str, "<=52"); 88 | TRY(in = Itv(5,10,">") + 42); TRY(str = std::format("{}", in)); TEST_EQUAL(str, ">=48"); 89 | TRY(in = Itv(5,10,">=") + 42); TRY(str = std::format("{}", in)); TEST_EQUAL(str, ">=47"); 90 | 91 | struct test_info { 92 | int line; 93 | Itv lhs; 94 | Itv rhs; 95 | Itv add; 96 | Itv sub1; 97 | Itv sub2; 98 | Itv mul; 99 | }; 100 | 101 | static const std::vector tests = { 102 | 103 | // line lhs rhs add sub1 sub2 mul -- 104 | { __LINE__, {}, {}, {}, {}, {}, {}, }, 105 | { __LINE__, {0,0,"*"}, {}, {}, {}, {}, {}, }, 106 | { __LINE__, {10}, {}, {}, {}, {}, {}, }, 107 | { __LINE__, {10,20,"[]"}, {}, {}, {}, {}, {}, }, 108 | { __LINE__, {10,10,"<="}, {}, {}, {}, {}, {}, }, 109 | { __LINE__, {10,10,">="}, {}, {}, {}, {}, {}, }, 110 | { __LINE__, {0,0,"*"}, {0,0,"*"}, {0,0,"*"}, {0,0,"*"}, {0,0,"*"}, {0,0,"*"}, }, 111 | { __LINE__, {10}, {0,0,"*"}, {0,0,"*"}, {0,0,"*"}, {0,0,"*"}, {0,0,"*"}, }, 112 | { __LINE__, {10,20,"[]"}, {0,0,"*"}, {0,0,"*"}, {0,0,"*"}, {0,0,"*"}, {0,0,"*"}, }, 113 | { __LINE__, {10,10,"<="}, {0,0,"*"}, {0,0,"*"}, {0,0,"*"}, {0,0,"*"}, {0,0,"*"}, }, 114 | { __LINE__, {10,10,">="}, {0,0,"*"}, {0,0,"*"}, {0,0,"*"}, {0,0,"*"}, {0,0,"*"}, }, 115 | { __LINE__, {10}, {42}, {52}, {-32}, {32}, {420}, }, 116 | { __LINE__, {10,20,"[]"}, {42}, {52,62,"[]"}, {-32,-22,"[]"}, {22,32,"[]"}, {420,840,"[]"}, }, 117 | { __LINE__, {10,10,"<="}, {42}, {52,52,"<="}, {-32,-32,"<="}, {32,32,">="}, {420,420,"<="}, }, 118 | { __LINE__, {10,10,">="}, {42}, {52,52,">="}, {-32,-32,">="}, {32,32,"<="}, {420,420,">="}, }, 119 | { __LINE__, {10,20,"[]"}, {-86,99,"[]"}, {-76,119,"[]"}, {-89,106,"[]"}, {-106,89,"[]"}, {-1720,1980,"[]"}, }, 120 | { __LINE__, {10,10,"<="}, {-86,99,"[]"}, {109,109,"<="}, {96,96,"<="}, {-96,-96,">="}, {0,0,"*"}, }, 121 | { __LINE__, {10,10,">="}, {-86,99,"[]"}, {-76,-76,">="}, {-89,-89,">="}, {89,89,"<="}, {0,0,"*"}, }, 122 | { __LINE__, {10,10,"<="}, {42,42,"<="}, {52,52,"<="}, {0,0,"*"}, {0,0,"*"}, {0,0,"*"}, }, 123 | { __LINE__, {10,10,">="}, {42,42,"<="}, {0,0,"*"}, {-32,-32,">="}, {32,32,"<="}, {0,0,"*"}, }, 124 | { __LINE__, {10,10,">="}, {42,42,">="}, {52,52,">="}, {0,0,"*"}, {0,0,"*"}, {420,420,">="}, }, 125 | 126 | }; 127 | 128 | Itv a, b, c; 129 | 130 | for (const auto& t: tests) { 131 | 132 | int errors = 0; 133 | 134 | TRY(a = t.lhs); 135 | TRY(b = t.rhs); 136 | 137 | TRY(c = a + b); TEST_EQUAL(c, t.add); errors += int(c != t.add); 138 | TRY(c = a - b); TEST_EQUAL(c, t.sub1); errors += int(c != t.sub1); 139 | TRY(c = b - a); TEST_EQUAL(c, t.sub2); errors += int(c != t.sub2); 140 | TRY(c = a * b); TEST_EQUAL(c, t.mul); errors += int(c != t.mul); 141 | TRY(c = a); TRY(c += b); TEST_EQUAL(c, t.add); errors += int(c != t.add); 142 | TRY(c = a); TRY(c -= b); TEST_EQUAL(c, t.sub1); errors += int(c != t.sub1); 143 | TRY(c = b); TRY(c -= a); TEST_EQUAL(c, t.sub2); errors += int(c != t.sub2); 144 | TRY(c = a); TRY(c *= b); TEST_EQUAL(c, t.mul); errors += int(c != t.mul); 145 | 146 | if (errors > 0) { 147 | std::println("... [{}] {} {}", t.line, t.lhs, t.rhs); 148 | } 149 | 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /src/test/integral-boundary-addition-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/types.hpp" 2 | #include "test/unit-test.hpp" 3 | 4 | using namespace RS::Interval; 5 | using namespace RS::Interval::Detail; 6 | 7 | using B = Boundary; 8 | using BT = BoundaryType; 9 | 10 | void test_rs_interval_integral_boundary_inversion() { 11 | 12 | static const B none = {0, BT::empty}; 13 | static const B ninf = {0, BT::minus_infinity}; 14 | static const B pinf = {0, BT::plus_infinity}; 15 | static const B cl2 = {2, BT::closed}; 16 | static const B op2 = {2, BT::open}; 17 | static const B cl_2 = {-2, BT::closed}; 18 | static const B op_2 = {-2, BT::open}; 19 | 20 | TEST_EQUAL(- none, none); 21 | TEST_EQUAL(- ninf, pinf); 22 | TEST_EQUAL(- pinf, ninf); 23 | TEST_EQUAL(- cl2, cl_2); 24 | TEST_EQUAL(- op2, op_2); 25 | TEST_EQUAL(- cl_2, cl2); 26 | TEST_EQUAL(- op_2, op2); 27 | 28 | } 29 | 30 | void test_rs_interval_integral_boundary_addition() { 31 | 32 | static const B none = {0, BT::empty}; 33 | static const B ninf = {0, BT::minus_infinity}; 34 | static const B pinf = {0, BT::plus_infinity}; 35 | static const B cl1 = {1, BT::closed}; 36 | static const B op1 = {1, BT::open}; 37 | static const B cl2 = {2, BT::closed}; 38 | static const B op2 = {2, BT::open}; 39 | static const B cl3 = {3, BT::closed}; 40 | static const B op3 = {3, BT::open}; 41 | 42 | TEST_EQUAL(none + none, none); 43 | TEST_EQUAL(none + ninf, none); 44 | TEST_EQUAL(none + pinf, none); 45 | TEST_EQUAL(none + cl2, none); 46 | TEST_EQUAL(none + op2, none); 47 | TEST_EQUAL(ninf + none, none); 48 | TEST_EQUAL(ninf + ninf, ninf); 49 | TEST_EQUAL(ninf + cl2, ninf); 50 | TEST_EQUAL(ninf + op2, ninf); 51 | TEST_EQUAL(pinf + none, none); 52 | TEST_EQUAL(pinf + pinf, pinf); 53 | TEST_EQUAL(pinf + cl2, pinf); 54 | TEST_EQUAL(cl1 + none, none); 55 | TEST_EQUAL(cl1 + ninf, ninf); 56 | TEST_EQUAL(cl1 + pinf, pinf); 57 | TEST_EQUAL(cl1 + cl2, cl3); 58 | TEST_EQUAL(cl1 + op2, op3); 59 | TEST_EQUAL(op1 + none, none); 60 | TEST_EQUAL(op1 + ninf, ninf); 61 | TEST_EQUAL(op1 + cl2, op3); 62 | TEST_EQUAL(op1 + op2, op3); 63 | 64 | } 65 | 66 | void test_rs_interval_integral_boundary_subtraction() { 67 | 68 | static const B none = {0, BT::empty}; 69 | static const B ninf = {0, BT::minus_infinity}; 70 | static const B pinf = {0, BT::plus_infinity}; 71 | static const B cl1 = {1, BT::closed}; 72 | static const B op1 = {1, BT::open}; 73 | static const B cl2 = {2, BT::closed}; 74 | static const B op2 = {2, BT::open}; 75 | static const B cl3 = {3, BT::closed}; 76 | static const B op3 = {3, BT::open}; 77 | 78 | TEST_EQUAL(none - none, none); 79 | TEST_EQUAL(none - ninf, none); 80 | TEST_EQUAL(none - pinf, none); 81 | TEST_EQUAL(none - cl2, none); 82 | TEST_EQUAL(none - op2, none); 83 | TEST_EQUAL(ninf - none, none); 84 | TEST_EQUAL(ninf - pinf, ninf); 85 | TEST_EQUAL(ninf - cl2, ninf); 86 | TEST_EQUAL(pinf - none, none); 87 | TEST_EQUAL(pinf - ninf, pinf); 88 | TEST_EQUAL(pinf - cl2, pinf); 89 | TEST_EQUAL(pinf - op2, pinf); 90 | TEST_EQUAL(cl3 - none, none); 91 | TEST_EQUAL(cl3 - ninf, pinf); 92 | TEST_EQUAL(cl3 - pinf, ninf); 93 | TEST_EQUAL(cl3 - cl2, cl1); 94 | TEST_EQUAL(cl3 - op2, op1); 95 | TEST_EQUAL(op3 - none, none); 96 | TEST_EQUAL(op3 - pinf, ninf); 97 | TEST_EQUAL(op3 - cl2, op1); 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/test/integral-boundary-basic-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/interval.hpp" 2 | #include "rs-interval/types.hpp" 3 | #include "test/unit-test.hpp" 4 | #include 5 | 6 | using namespace RS::Interval; 7 | using namespace RS::Interval::Detail; 8 | 9 | using Itv = Interval; 10 | using B = Boundary; 11 | using BT = BoundaryType; 12 | 13 | void test_rs_interval_integral_boundary_formatting() { 14 | 15 | B b; 16 | 17 | TRY((b = {42, BT::empty})); TEST_EQUAL(std::format("{}", b), "{}"); 18 | TRY((b = {42, BT::minus_infinity})); TEST_EQUAL(std::format("{}", b), "-inf"); 19 | TRY((b = {42, BT::plus_infinity})); TEST_EQUAL(std::format("{}", b), "+inf"); 20 | TRY((b = {42, BT::closed})); TEST_EQUAL(std::format("{}", b), "42"); 21 | 22 | } 23 | 24 | void test_rs_interval_integral_boundary_from_interval() { 25 | 26 | static const Itv none; 27 | static const Itv all = Itv::all(); 28 | static const Itv eq1(1); 29 | static const Itv ge1(1, 1, Bound::closed, Bound::unbound); 30 | static const Itv le1(1, 1, Bound::unbound, Bound::closed); 31 | static const Itv c14(1, 4, Bound::closed, Bound::closed); 32 | 33 | TEST_EQUAL(std::format("{}", none), "{}"); 34 | TEST_EQUAL(std::format("{}", all), "*"); 35 | TEST_EQUAL(std::format("{}", eq1), "1"); 36 | TEST_EQUAL(std::format("{}", ge1), ">=1"); 37 | TEST_EQUAL(std::format("{}", le1), "<=1"); 38 | TEST_EQUAL(std::format("{}", c14), "[1,4]"); 39 | 40 | TEST_EQUAL(left_boundary_of(none), (B{0, BT::empty})); 41 | TEST_EQUAL(left_boundary_of(all), (B{0, BT::minus_infinity})); 42 | TEST_EQUAL(left_boundary_of(eq1), (B{1, BT::closed})); 43 | TEST_EQUAL(left_boundary_of(ge1), (B{1, BT::closed})); 44 | TEST_EQUAL(left_boundary_of(le1), (B{0, BT::minus_infinity})); 45 | TEST_EQUAL(left_boundary_of(c14), (B{1, BT::closed})); 46 | 47 | TEST_EQUAL(right_boundary_of(none), (B{0, BT::empty})); 48 | TEST_EQUAL(right_boundary_of(all), (B{0, BT::plus_infinity})); 49 | TEST_EQUAL(right_boundary_of(eq1), (B{1, BT::closed})); 50 | TEST_EQUAL(right_boundary_of(ge1), (B{0, BT::plus_infinity})); 51 | TEST_EQUAL(right_boundary_of(le1), (B{1, BT::closed})); 52 | TEST_EQUAL(right_boundary_of(c14), (B{4, BT::closed})); 53 | 54 | } 55 | 56 | void test_rs_interval_integral_boundary_to_interval() { 57 | 58 | static const B none = {0, BT::empty}; 59 | static const B ninf = {0, BT::minus_infinity}; 60 | static const B pinf = {0, BT::plus_infinity}; 61 | static const B cl1 = {1, BT::closed}; 62 | static const B op1 = {1, BT::open}; 63 | static const B cl4 = {4, BT::closed}; 64 | static const B op4 = {4, BT::open}; 65 | 66 | TEST_EQUAL(interval_from_boundaries(none, none), Itv()); 67 | TEST_EQUAL(interval_from_boundaries(ninf, pinf), Itv::all()); 68 | TEST_EQUAL(interval_from_boundaries(ninf, cl1), Itv(1, 1, "<=")); 69 | TEST_EQUAL(interval_from_boundaries(ninf, op1), Itv(0, 0, "<=")); 70 | TEST_EQUAL(interval_from_boundaries(cl1, pinf), Itv(1, 1, ">=")); 71 | TEST_EQUAL(interval_from_boundaries(op1, pinf), Itv(2, 2, ">=")); 72 | TEST_EQUAL(interval_from_boundaries(cl1, cl1), Itv(1)); 73 | TEST_EQUAL(interval_from_boundaries(cl1, cl4), Itv(1, 4, "[]")); 74 | TEST_EQUAL(interval_from_boundaries(cl1, op4), Itv(1, 3, "[]")); 75 | TEST_EQUAL(interval_from_boundaries(op1, cl4), Itv(2, 4, "[]")); 76 | TEST_EQUAL(interval_from_boundaries(op1, op4), Itv(2, 3, "[]")); 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/test/integral-boundary-comparison-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/types.hpp" 2 | #include "test/unit-test.hpp" 3 | 4 | using namespace RS::Interval; 5 | using namespace RS::Interval::Detail; 6 | 7 | using B = Boundary; 8 | using BT = BoundaryType; 9 | 10 | void test_rs_interval_integral_boundary_adjacency() { 11 | 12 | static const B none = {0, BT::empty}; 13 | static const B ninf = {0, BT::minus_infinity}; 14 | static const B pinf = {0, BT::plus_infinity}; 15 | static const B cl1 = {1, BT::closed}; 16 | static const B cl2 = {2, BT::closed}; 17 | 18 | TEST(! none.adjacent(none)); 19 | TEST(! none.adjacent(ninf)); 20 | TEST(! none.adjacent(pinf)); 21 | TEST(! none.adjacent(cl1)); 22 | TEST(! ninf.adjacent(none)); 23 | TEST(! ninf.adjacent(ninf)); 24 | TEST(! ninf.adjacent(pinf)); 25 | TEST(! ninf.adjacent(cl1)); 26 | TEST(! pinf.adjacent(none)); 27 | TEST(! pinf.adjacent(ninf)); 28 | TEST(! pinf.adjacent(pinf)); 29 | TEST(! pinf.adjacent(cl1)); 30 | TEST(! cl1.adjacent(none)); 31 | TEST(! cl1.adjacent(ninf)); 32 | TEST(! cl1.adjacent(pinf)); 33 | TEST(! cl1.adjacent(cl1)); 34 | TEST(cl1.adjacent(cl2)); 35 | TEST(cl2.adjacent(cl1)); 36 | 37 | } 38 | 39 | void test_rs_interval_integral_boundary_comparison() { 40 | 41 | static const B none = {0, BT::empty}; 42 | static const B ninf = {0, BT::minus_infinity}; 43 | static const B pinf = {0, BT::plus_infinity}; 44 | static const B cl1 = {1, BT::closed}; 45 | static const B cl4 = {4, BT::closed}; 46 | 47 | TEST(! none.compare_ll(none)); 48 | TEST(none.compare_ll(ninf)); 49 | TEST(none.compare_ll(pinf)); 50 | TEST(none.compare_ll(cl1)); 51 | TEST(! ninf.compare_ll(none)); 52 | TEST(! ninf.compare_ll(ninf)); 53 | TEST(ninf.compare_ll(pinf)); 54 | TEST(ninf.compare_ll(cl1)); 55 | TEST(! pinf.compare_ll(none)); 56 | TEST(! pinf.compare_ll(ninf)); 57 | TEST(! pinf.compare_ll(pinf)); 58 | TEST(! pinf.compare_ll(cl1)); 59 | TEST(! cl1.compare_ll(none)); 60 | TEST(! cl1.compare_ll(ninf)); 61 | TEST(cl1.compare_ll(pinf)); 62 | TEST(! cl1.compare_ll(cl1)); 63 | TEST(cl1.compare_ll(cl4)); 64 | TEST(! cl4.compare_ll(cl1)); 65 | 66 | TEST(! none.compare_rr(none)); 67 | TEST(none.compare_rr(ninf)); 68 | TEST(none.compare_rr(pinf)); 69 | TEST(none.compare_rr(cl1)); 70 | TEST(! ninf.compare_rr(none)); 71 | TEST(! ninf.compare_rr(ninf)); 72 | TEST(ninf.compare_rr(pinf)); 73 | TEST(ninf.compare_rr(cl1)); 74 | TEST(! pinf.compare_rr(none)); 75 | TEST(! pinf.compare_rr(ninf)); 76 | TEST(! pinf.compare_rr(pinf)); 77 | TEST(! pinf.compare_rr(cl1)); 78 | TEST(! cl1.compare_rr(none)); 79 | TEST(! cl1.compare_rr(ninf)); 80 | TEST(cl1.compare_rr(pinf)); 81 | TEST(! cl1.compare_rr(cl1)); 82 | TEST(cl1.compare_rr(cl4)); 83 | TEST(! cl4.compare_rr(cl1)); 84 | 85 | TEST(! none.compare_lr(none)); 86 | TEST(none.compare_lr(ninf)); 87 | TEST(none.compare_lr(pinf)); 88 | TEST(none.compare_lr(cl1)); 89 | TEST(! ninf.compare_lr(none)); 90 | TEST(! ninf.compare_lr(ninf)); 91 | TEST(ninf.compare_lr(pinf)); 92 | TEST(ninf.compare_lr(cl1)); 93 | TEST(! pinf.compare_lr(none)); 94 | TEST(! pinf.compare_lr(ninf)); 95 | TEST(! pinf.compare_lr(pinf)); 96 | TEST(! pinf.compare_lr(cl1)); 97 | TEST(! cl1.compare_lr(none)); 98 | TEST(! cl1.compare_lr(ninf)); 99 | TEST(cl1.compare_lr(pinf)); 100 | TEST(! cl1.compare_lr(cl1)); 101 | TEST(cl1.compare_lr(cl4)); 102 | TEST(! cl4.compare_lr(cl1)); 103 | 104 | TEST(! none.compare_rl(none)); 105 | TEST(none.compare_rl(ninf)); 106 | TEST(none.compare_rl(pinf)); 107 | TEST(none.compare_rl(cl1)); 108 | TEST(! ninf.compare_rl(none)); 109 | TEST(! ninf.compare_rl(ninf)); 110 | TEST(ninf.compare_rl(pinf)); 111 | TEST(ninf.compare_rl(cl1)); 112 | TEST(! pinf.compare_rl(none)); 113 | TEST(! pinf.compare_rl(ninf)); 114 | TEST(! pinf.compare_rl(pinf)); 115 | TEST(! pinf.compare_rl(cl1)); 116 | TEST(! cl1.compare_rl(none)); 117 | TEST(! cl1.compare_rl(ninf)); 118 | TEST(cl1.compare_rl(pinf)); 119 | TEST(! cl1.compare_rl(cl1)); 120 | TEST(cl1.compare_rl(cl4)); 121 | TEST(! cl4.compare_rl(cl1)); 122 | 123 | } 124 | -------------------------------------------------------------------------------- /src/test/integral-boundary-multiplication-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/types.hpp" 2 | #include "test/unit-test.hpp" 3 | 4 | using namespace RS::Interval; 5 | using namespace RS::Interval::Detail; 6 | 7 | using B = Boundary; 8 | using BT = BoundaryType; 9 | 10 | void test_rs_interval_integral_boundary_multiplication() { 11 | 12 | static const B none = {0, BT::empty}; 13 | static const B ninf = {0, BT::minus_infinity}; 14 | static const B pinf = {0, BT::plus_infinity}; 15 | static const B cl0 = {0, BT::closed}; 16 | static const B cl2 = {2, BT::closed}; 17 | static const B cl3 = {3, BT::closed}; 18 | static const B cl6 = {6, BT::closed}; 19 | static const B cl_2 = {-2, BT::closed}; 20 | static const B cl_3 = {-3, BT::closed}; 21 | static const B cl_6 = {-6, BT::closed}; 22 | 23 | TEST_EQUAL(none * none, none); 24 | TEST_EQUAL(none * ninf, none); 25 | TEST_EQUAL(none * pinf, none); 26 | TEST_EQUAL(none * cl_3, none); 27 | TEST_EQUAL(none * cl3, none); 28 | TEST_EQUAL(none * cl0, none); 29 | TEST_EQUAL(ninf * none, none); 30 | TEST_EQUAL(ninf * ninf, pinf); 31 | TEST_EQUAL(ninf * pinf, ninf); 32 | TEST_EQUAL(ninf * cl_3, pinf); 33 | TEST_EQUAL(ninf * cl3, ninf); 34 | TEST_EQUAL(ninf * cl0, cl0); 35 | TEST_EQUAL(pinf * none, none); 36 | TEST_EQUAL(pinf * ninf, ninf); 37 | TEST_EQUAL(pinf * pinf, pinf); 38 | TEST_EQUAL(pinf * cl_3, ninf); 39 | TEST_EQUAL(pinf * cl3, pinf); 40 | TEST_EQUAL(pinf * cl0, cl0); 41 | TEST_EQUAL(cl_2 * none, none); 42 | TEST_EQUAL(cl_2 * ninf, pinf); 43 | TEST_EQUAL(cl_2 * pinf, ninf); 44 | TEST_EQUAL(cl_2 * cl_3, cl6); 45 | TEST_EQUAL(cl_2 * cl3, cl_6); 46 | TEST_EQUAL(cl_2 * cl0, cl0); 47 | TEST_EQUAL(cl2 * none, none); 48 | TEST_EQUAL(cl2 * ninf, ninf); 49 | TEST_EQUAL(cl2 * pinf, pinf); 50 | TEST_EQUAL(cl2 * cl_3, cl_6); 51 | TEST_EQUAL(cl2 * cl3, cl6); 52 | TEST_EQUAL(cl2 * cl0, cl0); 53 | TEST_EQUAL(cl0 * none, none); 54 | TEST_EQUAL(cl0 * ninf, cl0); 55 | TEST_EQUAL(cl0 * pinf, cl0); 56 | TEST_EQUAL(cl0 * cl_3, cl0); 57 | TEST_EQUAL(cl0 * cl3, cl0); 58 | TEST_EQUAL(cl0 * cl0, cl0); 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/test/integral-map-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/interval.hpp" 2 | #include "rs-interval/map.hpp" 3 | #include "rs-interval/set.hpp" 4 | #include "rs-interval/types.hpp" 5 | #include "test/unit-test.hpp" 6 | #include 7 | #include 8 | 9 | using namespace RS::Interval; 10 | 11 | using Itv = Interval; 12 | using Map = IntervalMap; 13 | 14 | void test_rs_interval_integral_map() { 15 | 16 | Map map; 17 | Map::iterator it; 18 | 19 | TEST(map.empty()); 20 | TEST_EQUAL(map.size(), 0u); 21 | TEST_EQUAL(map.default_value(), ""); 22 | TEST_EQUAL(map[42], ""); 23 | TEST_EQUAL(std::format("{}", map), "{}"); 24 | 25 | TRY(map.insert(Itv(2,2,"<="), "alpha")); 26 | TRY(map.insert(Itv(3,7,"()"), "bravo")); 27 | TRY(map.insert(Itv(8,8,">="), "charlie")); 28 | TEST_EQUAL(map.size(), 3u); 29 | TEST_EQUAL(map[1], "alpha"); 30 | TEST_EQUAL(map[2], "alpha"); 31 | TEST_EQUAL(map[3], ""); 32 | TEST_EQUAL(map[4], "bravo"); 33 | TEST_EQUAL(map[5], "bravo"); 34 | TEST_EQUAL(map[6], "bravo"); 35 | TEST_EQUAL(map[7], ""); 36 | TEST_EQUAL(map[8], "charlie"); 37 | TEST_EQUAL(map[9], "charlie"); 38 | TRY(map.default_value("nil")); 39 | TEST_EQUAL(map[1], "alpha"); 40 | TEST_EQUAL(map[2], "alpha"); 41 | TEST_EQUAL(map[3], "nil"); 42 | TEST_EQUAL(map[4], "bravo"); 43 | TEST_EQUAL(map[5], "bravo"); 44 | TEST_EQUAL(map[6], "bravo"); 45 | TEST_EQUAL(map[7], "nil"); 46 | TEST_EQUAL(map[8], "charlie"); 47 | TEST_EQUAL(map[9], "charlie"); 48 | TEST_EQUAL(std::format("{}", map), "{<=2:alpha,[4,6]:bravo,>=8:charlie}"); 49 | 50 | TEST(map.contains(1)); 51 | TEST(map.contains(2)); 52 | TEST(! map.contains(3)); 53 | TEST(map.contains(4)); 54 | TEST(map.contains(5)); 55 | TEST(map.contains(6)); 56 | TEST(! map.contains(7)); 57 | TEST(map.contains(8)); 58 | TEST(map.contains(9)); 59 | 60 | TRY(it = map.find(1)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "alpha"); 61 | TRY(it = map.find(2)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "alpha"); 62 | TRY(it = map.find(3)); TEST(it == map.end()); 63 | TRY(it = map.find(4)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 64 | TRY(it = map.find(5)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 65 | TRY(it = map.find(6)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 66 | TRY(it = map.find(7)); TEST(it == map.end()); 67 | TRY(it = map.find(8)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 68 | TRY(it = map.find(9)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 69 | 70 | TRY(it = map.lower_bound(1)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "alpha"); 71 | TRY(it = map.lower_bound(2)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "alpha"); 72 | TRY(it = map.lower_bound(3)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 73 | TRY(it = map.lower_bound(4)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 74 | TRY(it = map.lower_bound(5)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 75 | TRY(it = map.lower_bound(6)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 76 | TRY(it = map.lower_bound(7)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 77 | TRY(it = map.lower_bound(8)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 78 | TRY(it = map.lower_bound(9)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 79 | 80 | TRY(it = map.upper_bound(1)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 81 | TRY(it = map.upper_bound(2)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 82 | TRY(it = map.upper_bound(3)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 83 | TRY(it = map.upper_bound(4)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 84 | TRY(it = map.upper_bound(5)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 85 | TRY(it = map.upper_bound(6)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 86 | TRY(it = map.upper_bound(7)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 87 | TRY(it = map.upper_bound(8)); TEST(it == map.end()); 88 | TRY(it = map.upper_bound(9)); TEST(it == map.end()); 89 | 90 | TRY(map.clear()); 91 | TEST(map.empty()); 92 | TRY((map = { 93 | {Itv(20,20,"<="), "alpha"}, 94 | {Itv(30,70,"()"), "bravo"}, 95 | {Itv(80,80,">="), "charlie"}, 96 | })); 97 | TEST_EQUAL(map.size(), 3u); 98 | TEST_EQUAL(std::format("{}", map), "{<=20:alpha,[31,69]:bravo,>=80:charlie}"); 99 | 100 | TRY(map.clear()); 101 | TRY(map.default_value("nil")); 102 | TRY(map.insert(Itv(10,10,"<="), "alpha")); 103 | TRY(map.insert(Itv(1,5,"[]"), "bravo")); 104 | TRY(map.insert(Itv(5,6,"[]"), "charlie")); 105 | TRY(map.insert(Itv(2,3,"[]"), "delta")); 106 | TRY(map.insert(Itv(7,8,"[]"), "charlie")); 107 | TEST_EQUAL(map.size(), 6u); 108 | TEST_EQUAL(std::format("{}", map), 109 | "{<=0:alpha," 110 | "1:bravo," 111 | "[2,3]:delta," 112 | "4:bravo," 113 | "[5,8]:charlie," 114 | "[9,10]:alpha}"); 115 | TEST_EQUAL(map[0], "alpha"); 116 | TEST_EQUAL(map[1], "bravo"); 117 | TEST_EQUAL(map[2], "delta"); 118 | TEST_EQUAL(map[3], "delta"); 119 | TEST_EQUAL(map[4], "bravo"); 120 | TEST_EQUAL(map[5], "charlie"); 121 | TEST_EQUAL(map[6], "charlie"); 122 | TEST_EQUAL(map[7], "charlie"); 123 | TEST_EQUAL(map[8], "charlie"); 124 | TEST_EQUAL(map[9], "alpha"); 125 | TEST_EQUAL(map[10], "alpha"); 126 | TEST_EQUAL(map[11], "nil"); 127 | TEST_EQUAL(map[12], "nil"); 128 | 129 | TRY(map.erase(Itv(1,2,"[]"))); 130 | TRY(map.erase(Itv(6,7,"[]"))); 131 | TRY(map.erase(Itv(10,10,">="))); 132 | TEST_EQUAL(map.size(), 6u); 133 | TEST_EQUAL(std::format("{}", map), 134 | "{<=0:alpha," 135 | "3:delta," 136 | "4:bravo," 137 | "5:charlie," 138 | "8:charlie," 139 | "9:alpha}"); 140 | TEST_EQUAL(map[0], "alpha"); 141 | TEST_EQUAL(map[1], "nil"); 142 | TEST_EQUAL(map[2], "nil"); 143 | TEST_EQUAL(map[3], "delta"); 144 | TEST_EQUAL(map[4], "bravo"); 145 | TEST_EQUAL(map[5], "charlie"); 146 | TEST_EQUAL(map[6], "nil"); 147 | TEST_EQUAL(map[7], "nil"); 148 | TEST_EQUAL(map[8], "charlie"); 149 | TEST_EQUAL(map[9], "alpha"); 150 | TEST_EQUAL(map[10], "nil"); 151 | TEST_EQUAL(map[11], "nil"); 152 | TEST_EQUAL(map[12], "nil"); 153 | 154 | } 155 | -------------------------------------------------------------------------------- /src/test/integral-set-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/interval.hpp" 2 | #include "rs-interval/set.hpp" 3 | #include "rs-interval/types.hpp" 4 | #include "test/unit-test.hpp" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace RS::Interval; 12 | 13 | using Itv = Interval; 14 | using Set = IntervalSet; 15 | 16 | void test_rs_interval_integral_set_construct_insert_erase() { 17 | 18 | Set set, com; 19 | Set::iterator it; 20 | Itv in; 21 | std::string str; 22 | 23 | TEST(set.empty()); 24 | TRY(set = 42); 25 | TEST_EQUAL(set.size(), 1u); 26 | TRY(it = set.begin()); 27 | TEST_EQUAL(std::format("{}", *it), "42"); 28 | TRY(++it); 29 | TEST(it == set.end()); 30 | TRY((in = {5,10})); 31 | TRY(set = in); 32 | TEST_EQUAL(set.size(), 1u); 33 | TRY(it = set.begin()); 34 | TEST_EQUAL(std::format("{}", *it), "[5,10]"); 35 | TRY(++it); 36 | TEST(it == set.end()); 37 | TRY((set = {{5,10},{15,20},{25,30}})); 38 | TRY(it = set.begin()); 39 | TEST_EQUAL(std::format("{}", *it), "[5,10]"); 40 | TRY(++it); 41 | TEST_EQUAL(std::format("{}", *it), "[15,20]"); 42 | TRY(++it); 43 | TEST_EQUAL(std::format("{}", *it), "[25,30]"); 44 | TRY(++it); 45 | TEST(it == set.end()); 46 | 47 | TRY((set = {})); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{}"); 48 | TRY((set = {{3,6,"()"}})); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[4,5]}"); 49 | TRY((set = {{0,5,"()"},{10,15,"()"},{20,25,"()"}})); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[1,4],[11,14],[21,24]}"); 50 | 51 | TRY((set = {{3,6},{9,12},{15,18}})); 52 | TRY(com = set.complement()); 53 | TRY(str = std::format("{}", set)); 54 | TEST_EQUAL(str, "{[3,6],[9,12],[15,18]}"); 55 | TRY(str = std::format("{}", com)); 56 | TEST_EQUAL(str, "{<=2,[7,8],[13,14],>=19}"); 57 | 58 | TEST(! set.contains(1)); TEST(com.contains(1)); 59 | TEST(! set.contains(2)); TEST(com.contains(2)); 60 | TEST(set.contains(3)); TEST(! com.contains(3)); 61 | TEST(set.contains(4)); TEST(! com.contains(4)); 62 | TEST(set.contains(5)); TEST(! com.contains(5)); 63 | TEST(set.contains(6)); TEST(! com.contains(6)); 64 | TEST(! set.contains(7)); TEST(com.contains(7)); 65 | TEST(! set.contains(8)); TEST(com.contains(8)); 66 | TEST(set.contains(9)); TEST(! com.contains(9)); 67 | TEST(set.contains(10)); TEST(! com.contains(10)); 68 | TEST(set.contains(11)); TEST(! com.contains(11)); 69 | TEST(set.contains(12)); TEST(! com.contains(12)); 70 | TEST(! set.contains(13)); TEST(com.contains(13)); 71 | TEST(! set.contains(14)); TEST(com.contains(14)); 72 | TEST(set.contains(15)); TEST(! com.contains(15)); 73 | TEST(set.contains(16)); TEST(! com.contains(16)); 74 | TEST(set.contains(17)); TEST(! com.contains(17)); 75 | TEST(set.contains(18)); TEST(! com.contains(18)); 76 | TEST(! set.contains(19)); TEST(com.contains(19)); 77 | TEST(! set.contains(20)); TEST(com.contains(20)); 78 | 79 | TRY(set.clear()); 80 | TEST(set.empty()); 81 | 82 | TRY((set.insert({10,20}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[10,20]}"); 83 | TRY((set.insert({20,30,"()"}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[10,29]}"); 84 | TRY((set.erase({5,10,"()"}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[10,29]}"); 85 | TRY((set.erase({5,10}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[11,29]}"); 86 | TRY((set.erase({12,14}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{11,[15,29]}"); 87 | TRY((set.erase({16,18,"()"}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{11,[15,16],[18,29]}"); 88 | TRY((set.insert({9,11}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[9,11],[15,16],[18,29]}"); 89 | TRY((set.insert({29,31,"()"}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[9,11],[15,16],[18,30]}"); 90 | 91 | } 92 | 93 | void test_rs_interval_integral_set_formatting() { 94 | 95 | Set set; 96 | std::string str; 97 | 98 | TRY((set = {})); TRY(str = std::format("{:04x}", set)); TEST_EQUAL(str, "{}"); 99 | TRY((set = {{3,6,"()"}})); TRY(str = std::format("{:04x}", set)); TEST_EQUAL(str, "{[0004,0005]}"); 100 | TRY((set = {{0,5,"()"},{10,15,"()"},{20,25,"()"}})); TRY(str = std::format("{:04x}", set)); TEST_EQUAL(str, "{[0001,0004],[000b,000e],[0015,0018]}"); 101 | 102 | } 103 | 104 | void test_rs_interval_integral_set_operations() { 105 | 106 | using random_int = std::uniform_int_distribution; 107 | 108 | Set set[2], i_set, u_set, d_set, sd_set; 109 | std::vector vec[2]; 110 | Set::iterator it; 111 | Itv in; 112 | std::minstd_rand rng(42); 113 | 114 | static constexpr int iterations = 1000; 115 | static constexpr int max_size = 10; 116 | static constexpr int max_value = 50; 117 | 118 | for (int i = 0; i < iterations; ++i) { 119 | 120 | for (int j = 0; j < 2; ++j) { 121 | 122 | TRY(set[j].clear()); 123 | vec[j].clear(); 124 | int size = random_int(1, max_size)(rng); 125 | 126 | for (int k = 0; k < size; ++k) { 127 | 128 | int a = random_int(1, max_value)(rng); 129 | int b = random_int(1, max_value)(rng); 130 | auto l = Bound(random_int(0, 3)(rng)); 131 | auto r = Bound(random_int(0, 3)(rng)); 132 | 133 | if ((l == Bound::empty) == (r == Bound::empty)) { 134 | TRY(in = Itv(a, b, l, r)); 135 | TRY(set[j].insert(in)); 136 | vec[j].push_back(in); 137 | } 138 | 139 | } 140 | 141 | } 142 | 143 | TRY(i_set = set_intersection(set[0], set[1])); 144 | TRY(u_set = set_union(set[0], set[1])); 145 | TRY(d_set = set_difference(set[0], set[1])); 146 | TRY(sd_set = set_symmetric_difference(set[0], set[1])); 147 | 148 | for (int x = 0; x <= max_value + 1; ++x) { 149 | 150 | bool member[2]; 151 | 152 | for (int j = 0; j < 2; ++j) { 153 | member[j] = false; 154 | for (const auto& item: vec[j]) { 155 | member[j] |= item(x); 156 | } 157 | } 158 | 159 | auto i_expect = member[0] && member[1]; 160 | auto u_expect = member[0] || member[1]; 161 | auto d_expect = member[0] && ! member[1]; 162 | auto sd_expect = member[0] != member[1]; 163 | auto i_test = false; 164 | auto u_test = false; 165 | auto d_test = false; 166 | auto sd_test = false; 167 | 168 | TRY(i_test = i_set[x]); 169 | TRY(u_test = u_set[x]); 170 | TRY(d_test = d_set[x]); 171 | TRY(sd_test = sd_set[x]); 172 | TEST_EQUAL(i_test, i_expect); 173 | TEST_EQUAL(u_test, u_expect); 174 | TEST_EQUAL(d_test, d_expect); 175 | TEST_EQUAL(sd_test, sd_expect); 176 | 177 | if ((i_test != i_expect) || (u_test != u_expect) || (d_test != d_expect) || (sd_test != sd_expect)) { 178 | for (int j = 0; j < 2; ++j) { 179 | std::println("... {} => {}", vec[j], set[j]); 180 | } 181 | std::println("... x={}", x); 182 | } 183 | 184 | } 185 | 186 | TRY(i_set = set[0]); 187 | TRY(u_set = set[0]); 188 | TRY(d_set = set[0]); 189 | TRY(sd_set = set[0]); 190 | TRY(i_set.apply_intersection(set[1])); 191 | TRY(u_set.apply_union(set[1])); 192 | TRY(d_set.apply_difference(set[1])); 193 | TRY(sd_set.apply_symmetric_difference(set[1])); 194 | 195 | for (int x = 0; x <= max_value + 1; ++x) { 196 | 197 | bool member[2]; 198 | 199 | for (int j = 0; j < 2; ++j) { 200 | member[j] = false; 201 | for (const auto& item: vec[j]) { 202 | member[j] |= item(x); 203 | } 204 | } 205 | 206 | auto i_expect = member[0] && member[1]; 207 | auto u_expect = member[0] || member[1]; 208 | auto d_expect = member[0] && ! member[1]; 209 | auto sd_expect = member[0] != member[1]; 210 | auto i_test = false; 211 | auto u_test = false; 212 | auto d_test = false; 213 | auto sd_test = false; 214 | 215 | TRY(i_test = i_set[x]); 216 | TRY(u_test = u_set[x]); 217 | TRY(d_test = d_set[x]); 218 | TRY(sd_test = sd_set[x]); 219 | TEST_EQUAL(i_test, i_expect); 220 | TEST_EQUAL(u_test, u_expect); 221 | TEST_EQUAL(d_test, d_expect); 222 | TEST_EQUAL(sd_test, sd_expect); 223 | 224 | if ((i_test != i_expect) || (u_test != u_expect) || (d_test != d_expect) || (sd_test != sd_expect)) { 225 | for (int j = 0; j < 2; ++j) { 226 | std::println("... {} => {}", vec[j], set[j]); 227 | } 228 | std::println("... x={}", x); 229 | } 230 | 231 | } 232 | 233 | } 234 | 235 | } 236 | -------------------------------------------------------------------------------- /src/test/ordered-boundary-basic-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/interval.hpp" 2 | #include "rs-interval/types.hpp" 3 | #include "test/unit-test.hpp" 4 | #include 5 | #include 6 | 7 | using namespace RS::Interval; 8 | using namespace RS::Interval::Detail; 9 | 10 | using Itv = Interval; 11 | using B = Boundary; 12 | using BT = BoundaryType; 13 | 14 | void test_rs_interval_ordered_boundary_formatting() { 15 | 16 | B b; 17 | 18 | TRY((b = {"abc", BT::empty})); TEST_EQUAL(std::format("{}", b), "{}"); 19 | TRY((b = {"abc", BT::minus_infinity})); TEST_EQUAL(std::format("{}", b), "-inf"); 20 | TRY((b = {"abc", BT::plus_infinity})); TEST_EQUAL(std::format("{}", b), "+inf"); 21 | TRY((b = {"abc", BT::closed})); TEST_EQUAL(std::format("{}", b), "abc"); 22 | TRY((b = {"abc", BT::open})); TEST_EQUAL(std::format("{}", b), "(abc)"); 23 | 24 | } 25 | 26 | void test_rs_interval_ordered_boundary_from_interval() { 27 | 28 | static const Itv none; 29 | static const Itv all = Itv::all(); 30 | static const Itv eq1("a"); 31 | static const Itv ge1("a", "a", Bound::closed, Bound::unbound); 32 | static const Itv gt1("a", "a", Bound::open, Bound::unbound); 33 | static const Itv le1("a", "a", Bound::unbound, Bound::closed); 34 | static const Itv lt1("a", "a", Bound::unbound, Bound::open); 35 | static const Itv c12("a", "b", Bound::closed, Bound::closed); 36 | static const Itv o12("a", "b", Bound::open, Bound::open); 37 | static const Itv o1c2("a", "b", Bound::open, Bound::closed); 38 | static const Itv c1o2("a", "b", Bound::closed, Bound::open); 39 | 40 | TEST_EQUAL(std::format("{}", none), "{}"); 41 | TEST_EQUAL(std::format("{}", all), "*"); 42 | TEST_EQUAL(std::format("{}", eq1), "a"); 43 | TEST_EQUAL(std::format("{}", ge1), ">=a"); 44 | TEST_EQUAL(std::format("{}", gt1), ">a"); 45 | TEST_EQUAL(std::format("{}", le1), "<=a"); 46 | TEST_EQUAL(std::format("{}", lt1), " 4 | 5 | using namespace RS::Interval; 6 | using namespace RS::Interval::Detail; 7 | 8 | using B = Boundary; 9 | using BT = BoundaryType; 10 | 11 | void test_rs_interval_ordered_boundary_adjacency() { 12 | 13 | static const B none = {"", BT::empty}; 14 | static const B ninf = {"", BT::minus_infinity}; 15 | static const B pinf = {"", BT::plus_infinity}; 16 | static const B cl1 = {"a", BT::closed}; 17 | static const B op1 = {"a", BT::open}; 18 | static const B cl2 = {"b", BT::closed}; 19 | static const B op2 = {"b", BT::open}; 20 | 21 | TEST(! none.adjacent(none)); 22 | TEST(! none.adjacent(ninf)); 23 | TEST(! none.adjacent(pinf)); 24 | TEST(! none.adjacent(cl1)); 25 | TEST(! none.adjacent(op1)); 26 | TEST(! ninf.adjacent(none)); 27 | TEST(! ninf.adjacent(ninf)); 28 | TEST(! ninf.adjacent(pinf)); 29 | TEST(! ninf.adjacent(cl1)); 30 | TEST(! ninf.adjacent(op1)); 31 | TEST(! pinf.adjacent(none)); 32 | TEST(! pinf.adjacent(ninf)); 33 | TEST(! pinf.adjacent(pinf)); 34 | TEST(! pinf.adjacent(cl1)); 35 | TEST(! pinf.adjacent(op1)); 36 | TEST(! cl1.adjacent(none)); 37 | TEST(! cl1.adjacent(ninf)); 38 | TEST(! cl1.adjacent(pinf)); 39 | TEST(! cl1.adjacent(cl1)); 40 | TEST(cl1.adjacent(op1)); 41 | TEST(! cl1.adjacent(cl2)); 42 | TEST(! cl1.adjacent(op2)); 43 | TEST(! op1.adjacent(none)); 44 | TEST(! op1.adjacent(ninf)); 45 | TEST(! op1.adjacent(pinf)); 46 | TEST(op1.adjacent(cl1)); 47 | TEST(! op1.adjacent(op1)); 48 | TEST(! op1.adjacent(cl2)); 49 | TEST(! op1.adjacent(op2)); 50 | TEST(! cl2.adjacent(cl1)); 51 | TEST(! cl2.adjacent(op1)); 52 | TEST(! op2.adjacent(cl1)); 53 | TEST(! op2.adjacent(op1)); 54 | 55 | } 56 | 57 | void test_rs_interval_ordered_boundary_comparison() { 58 | 59 | static const B none = {"", BT::empty}; 60 | static const B ninf = {"", BT::minus_infinity}; 61 | static const B pinf = {"", BT::plus_infinity}; 62 | static const B cl1 = {"a", BT::closed}; 63 | static const B op1 = {"a", BT::open}; 64 | static const B cl2 = {"b", BT::closed}; 65 | static const B op2 = {"b", BT::open}; 66 | 67 | TEST(! none.compare_ll(none)); 68 | TEST(none.compare_ll(ninf)); 69 | TEST(none.compare_ll(pinf)); 70 | TEST(none.compare_ll(cl1)); 71 | TEST(none.compare_ll(op1)); 72 | TEST(! ninf.compare_ll(none)); 73 | TEST(! ninf.compare_ll(ninf)); 74 | TEST(ninf.compare_ll(pinf)); 75 | TEST(ninf.compare_ll(cl1)); 76 | TEST(ninf.compare_ll(op1)); 77 | TEST(! pinf.compare_ll(none)); 78 | TEST(! pinf.compare_ll(ninf)); 79 | TEST(! pinf.compare_ll(pinf)); 80 | TEST(! pinf.compare_ll(cl1)); 81 | TEST(! pinf.compare_ll(op1)); 82 | TEST(! cl1.compare_ll(none)); 83 | TEST(! cl1.compare_ll(ninf)); 84 | TEST(cl1.compare_ll(pinf)); 85 | TEST(! cl1.compare_ll(cl1)); 86 | TEST(cl1.compare_ll(op1)); 87 | TEST(cl1.compare_ll(cl2)); 88 | TEST(cl1.compare_ll(op2)); 89 | TEST(! op1.compare_ll(none)); 90 | TEST(! op1.compare_ll(ninf)); 91 | TEST(op1.compare_ll(pinf)); 92 | TEST(! op1.compare_ll(cl1)); 93 | TEST(! op1.compare_ll(op1)); 94 | TEST(op1.compare_ll(cl2)); 95 | TEST(op1.compare_ll(op2)); 96 | TEST(! cl2.compare_ll(cl1)); 97 | TEST(! cl2.compare_ll(op1)); 98 | TEST(! op2.compare_ll(cl1)); 99 | TEST(! op2.compare_ll(op1)); 100 | 101 | TEST(! none.compare_rr(none)); 102 | TEST(none.compare_rr(ninf)); 103 | TEST(none.compare_rr(pinf)); 104 | TEST(none.compare_rr(cl1)); 105 | TEST(none.compare_rr(op1)); 106 | TEST(! ninf.compare_rr(none)); 107 | TEST(! ninf.compare_rr(ninf)); 108 | TEST(ninf.compare_rr(pinf)); 109 | TEST(ninf.compare_rr(cl1)); 110 | TEST(ninf.compare_rr(op1)); 111 | TEST(! pinf.compare_rr(none)); 112 | TEST(! pinf.compare_rr(ninf)); 113 | TEST(! pinf.compare_rr(pinf)); 114 | TEST(! pinf.compare_rr(cl1)); 115 | TEST(! pinf.compare_rr(op1)); 116 | TEST(! cl1.compare_rr(none)); 117 | TEST(! cl1.compare_rr(ninf)); 118 | TEST(cl1.compare_rr(pinf)); 119 | TEST(! cl1.compare_rr(cl1)); 120 | TEST(! cl1.compare_rr(op1)); 121 | TEST(cl1.compare_rr(cl2)); 122 | TEST(cl1.compare_rr(op2)); 123 | TEST(! op1.compare_rr(none)); 124 | TEST(! op1.compare_rr(ninf)); 125 | TEST(op1.compare_rr(pinf)); 126 | TEST(op1.compare_rr(cl1)); 127 | TEST(! op1.compare_rr(op1)); 128 | TEST(op1.compare_rr(cl2)); 129 | TEST(op1.compare_rr(op2)); 130 | TEST(! cl2.compare_rr(cl1)); 131 | TEST(! cl2.compare_rr(op1)); 132 | TEST(! op2.compare_rr(cl1)); 133 | TEST(! op2.compare_rr(op1)); 134 | 135 | TEST(! none.compare_lr(none)); 136 | TEST(none.compare_lr(ninf)); 137 | TEST(none.compare_lr(pinf)); 138 | TEST(none.compare_lr(cl1)); 139 | TEST(none.compare_lr(op1)); 140 | TEST(! ninf.compare_lr(none)); 141 | TEST(! ninf.compare_lr(ninf)); 142 | TEST(ninf.compare_lr(pinf)); 143 | TEST(ninf.compare_lr(cl1)); 144 | TEST(ninf.compare_lr(op1)); 145 | TEST(! pinf.compare_lr(none)); 146 | TEST(! pinf.compare_lr(ninf)); 147 | TEST(! pinf.compare_lr(pinf)); 148 | TEST(! pinf.compare_lr(cl1)); 149 | TEST(! pinf.compare_lr(op1)); 150 | TEST(! cl1.compare_lr(none)); 151 | TEST(! cl1.compare_lr(ninf)); 152 | TEST(cl1.compare_lr(pinf)); 153 | TEST(! cl1.compare_lr(cl1)); 154 | TEST(! cl1.compare_lr(op1)); 155 | TEST(cl1.compare_lr(cl2)); 156 | TEST(cl1.compare_lr(op2)); 157 | TEST(! op1.compare_lr(none)); 158 | TEST(! op1.compare_lr(ninf)); 159 | TEST(op1.compare_lr(pinf)); 160 | TEST(! op1.compare_lr(cl1)); 161 | TEST(! op1.compare_lr(op1)); 162 | TEST(op1.compare_lr(cl2)); 163 | TEST(op1.compare_lr(op2)); 164 | TEST(! cl2.compare_lr(cl1)); 165 | TEST(! cl2.compare_lr(op1)); 166 | TEST(! op2.compare_lr(cl1)); 167 | TEST(! op2.compare_lr(op1)); 168 | 169 | TEST(! none.compare_rl(none)); 170 | TEST(none.compare_rl(ninf)); 171 | TEST(none.compare_rl(pinf)); 172 | TEST(none.compare_rl(cl1)); 173 | TEST(none.compare_rl(op1)); 174 | TEST(! ninf.compare_rl(none)); 175 | TEST(! ninf.compare_rl(ninf)); 176 | TEST(ninf.compare_rl(pinf)); 177 | TEST(ninf.compare_rl(cl1)); 178 | TEST(ninf.compare_rl(op1)); 179 | TEST(! pinf.compare_rl(none)); 180 | TEST(! pinf.compare_rl(ninf)); 181 | TEST(! pinf.compare_rl(pinf)); 182 | TEST(! pinf.compare_rl(cl1)); 183 | TEST(! pinf.compare_rl(op1)); 184 | TEST(! cl1.compare_rl(none)); 185 | TEST(! cl1.compare_rl(ninf)); 186 | TEST(cl1.compare_rl(pinf)); 187 | TEST(! cl1.compare_rl(cl1)); 188 | TEST(cl1.compare_rl(op1)); 189 | TEST(cl1.compare_rl(cl2)); 190 | TEST(cl1.compare_rl(op2)); 191 | TEST(! op1.compare_rl(none)); 192 | TEST(! op1.compare_rl(ninf)); 193 | TEST(op1.compare_rl(pinf)); 194 | TEST(op1.compare_rl(cl1)); 195 | TEST(op1.compare_rl(op1)); 196 | TEST(op1.compare_rl(cl2)); 197 | TEST(op1.compare_rl(op2)); 198 | TEST(! cl2.compare_rl(cl1)); 199 | TEST(! cl2.compare_rl(op1)); 200 | TEST(! op2.compare_rl(cl1)); 201 | TEST(! op2.compare_rl(op1)); 202 | 203 | } 204 | -------------------------------------------------------------------------------- /src/test/ordered-map-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/interval.hpp" 2 | #include "rs-interval/map.hpp" 3 | #include "rs-interval/set.hpp" 4 | #include "rs-interval/types.hpp" 5 | #include "test/unit-test.hpp" 6 | #include 7 | #include 8 | 9 | using namespace RS::Interval; 10 | 11 | using Itv = Interval; 12 | using Map = IntervalMap; 13 | 14 | void test_rs_interval_ordered_map() { 15 | 16 | Map map; 17 | Map::iterator it; 18 | 19 | TEST(map.empty()); 20 | TEST_EQUAL(map.size(), 0u); 21 | TEST_EQUAL(map.default_value(), ""); 22 | TEST_EQUAL(map["hello"], ""); 23 | TEST_EQUAL(std::format("{}", map), "{}"); 24 | 25 | TRY(map.insert(Itv("b","b","<="), "alpha")); 26 | TRY(map.insert(Itv("c","g","()"), "bravo")); 27 | TRY(map.insert(Itv("h","h",">="), "charlie")); 28 | TEST_EQUAL(map.size(), 3u); 29 | TEST_EQUAL(map["a"], "alpha"); 30 | TEST_EQUAL(map["b"], "alpha"); 31 | TEST_EQUAL(map["c"], ""); 32 | TEST_EQUAL(map["d"], "bravo"); 33 | TEST_EQUAL(map["e"], "bravo"); 34 | TEST_EQUAL(map["f"], "bravo"); 35 | TEST_EQUAL(map["g"], ""); 36 | TEST_EQUAL(map["h"], "charlie"); 37 | TEST_EQUAL(map["i"], "charlie"); 38 | TRY(map.default_value("nil")); 39 | TEST_EQUAL(map["a"], "alpha"); 40 | TEST_EQUAL(map["b"], "alpha"); 41 | TEST_EQUAL(map["c"], "nil"); 42 | TEST_EQUAL(map["d"], "bravo"); 43 | TEST_EQUAL(map["e"], "bravo"); 44 | TEST_EQUAL(map["f"], "bravo"); 45 | TEST_EQUAL(map["g"], "nil"); 46 | TEST_EQUAL(map["h"], "charlie"); 47 | TEST_EQUAL(map["i"], "charlie"); 48 | TEST_EQUAL(std::format("{}", map), "{<=b:alpha,(c,g):bravo,>=h:charlie}"); 49 | 50 | TEST(map.contains("a")); 51 | TEST(map.contains("b")); 52 | TEST(! map.contains("c")); 53 | TEST(map.contains("d")); 54 | TEST(map.contains("e")); 55 | TEST(map.contains("f")); 56 | TEST(! map.contains("g")); 57 | TEST(map.contains("h")); 58 | TEST(map.contains("i")); 59 | 60 | TRY(it = map.find("a")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "alpha"); 61 | TRY(it = map.find("b")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "alpha"); 62 | TRY(it = map.find("c")); TEST(it == map.end()); 63 | TRY(it = map.find("d")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 64 | TRY(it = map.find("e")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 65 | TRY(it = map.find("f")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 66 | TRY(it = map.find("g")); TEST(it == map.end()); 67 | TRY(it = map.find("h")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 68 | TRY(it = map.find("i")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 69 | 70 | TRY(it = map.lower_bound("a")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "alpha"); 71 | TRY(it = map.lower_bound("b")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "alpha"); 72 | TRY(it = map.lower_bound("c")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 73 | TRY(it = map.lower_bound("d")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 74 | TRY(it = map.lower_bound("e")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 75 | TRY(it = map.lower_bound("f")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 76 | TRY(it = map.lower_bound("g")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 77 | TRY(it = map.lower_bound("h")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 78 | TRY(it = map.lower_bound("i")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 79 | 80 | TRY(it = map.upper_bound("a")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 81 | TRY(it = map.upper_bound("b")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 82 | TRY(it = map.upper_bound("c")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 83 | TRY(it = map.upper_bound("d")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 84 | TRY(it = map.upper_bound("e")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 85 | TRY(it = map.upper_bound("f")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 86 | TRY(it = map.upper_bound("g")); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 87 | TRY(it = map.upper_bound("h")); TEST(it == map.end()); 88 | TRY(it = map.upper_bound("i")); TEST(it == map.end()); 89 | 90 | TRY(map.clear()); 91 | TEST(map.empty()); 92 | TRY((map = { 93 | {Itv("bb","bb","<="), "alpha"}, 94 | {Itv("cc","gg","()"), "bravo"}, 95 | {Itv("hh","hh",">="), "charlie"}, 96 | })); 97 | TEST_EQUAL(map.size(), 3u); 98 | TEST_EQUAL(std::format("{}", map), "{<=bb:alpha,(cc,gg):bravo,>=hh:charlie}"); 99 | 100 | TRY(map.clear()); 101 | TRY(map.default_value("nil")); 102 | TRY(map.insert(Itv("j","j","<="), "alpha")); 103 | TRY(map.insert(Itv("a","e","[]"), "bravo")); 104 | TRY(map.insert(Itv("e","f","[]"), "charlie")); 105 | TRY(map.insert(Itv("a","d","()"), "delta")); 106 | TRY(map.insert(Itv("f","h","[]"), "charlie")); 107 | TEST_EQUAL(map.size(), 6u); 108 | TEST_EQUAL(std::format("{}", map), 109 | "{="))); 132 | TEST_EQUAL(map.size(), 6u); 133 | TEST_EQUAL(std::format("{}", map), 134 | "{ 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace RS::Interval; 12 | using namespace std::literals; 13 | 14 | using Itv = Interval; 15 | using Set = IntervalSet; 16 | 17 | void test_rs_interval_ordered_set_construct_insert_erase() { 18 | 19 | Set set, com; 20 | Set::iterator it; 21 | Itv in; 22 | std::string str; 23 | 24 | TEST(set.empty()); 25 | TRY(set = "hello"s); 26 | TEST_EQUAL(set.size(), 1u); 27 | TRY(it = set.begin()); 28 | TEST_EQUAL(std::format("{}", *it), "hello"); 29 | TRY(++it); 30 | TEST(it == set.end()); 31 | TRY((in = {"hello","world"})); 32 | TRY(set = in); 33 | TEST_EQUAL(set.size(), 1u); 34 | TRY(it = set.begin()); 35 | TEST_EQUAL(std::format("{}", *it), "[hello,world]"); 36 | TRY(++it); 37 | TEST(it == set.end()); 38 | TRY((set = {{"abc","def"},{"ghi","jkl"},{"mno","pqr"}})); 39 | TRY(it = set.begin()); 40 | TEST_EQUAL(std::format("{}", *it), "[abc,def]"); 41 | TRY(++it); 42 | TEST_EQUAL(std::format("{}", *it), "[ghi,jkl]"); 43 | TRY(++it); 44 | TEST_EQUAL(std::format("{}", *it), "[mno,pqr]"); 45 | TRY(++it); 46 | TEST(it == set.end()); 47 | 48 | TRY((set = {})); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{}"); 49 | TRY((set = {{"a","b","()"}})); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{(a,b)}"); 50 | TRY((set = {{"a","b","()"},{"c","d","()"},{"e","f","()"}})); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{(a,b),(c,d),(e,f)}"); 51 | TRY((set = {{"a","b","[]"},{"c","d","[]"},{"e","f","[]"}})); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[a,b],[c,d],[e,f]}"); 52 | 53 | TRY((set = {{"a","b","[)"},{"c","d","(]"},{"e","f","()"}})); 54 | TRY(com = set.complement()); 55 | TRY(str = std::format("{}", set)); 56 | TEST_EQUAL(str, "{[a,b),(c,d],(e,f)}"); 57 | TRY(str = std::format("{}", com)); 58 | TEST_EQUAL(str, "{=f}"); 59 | 60 | TEST(! set.contains("@x")); TEST(com.contains("@x")); 61 | TEST(set.contains("a")); TEST(! com.contains("a")); 62 | TEST(set.contains("ax")); TEST(! com.contains("ax")); 63 | TEST(! set.contains("b")); TEST(com.contains("b")); 64 | TEST(! set.contains("bx")); TEST(com.contains("bx")); 65 | TEST(! set.contains("c")); TEST(com.contains("c")); 66 | TEST(set.contains("cx")); TEST(! com.contains("cx")); 67 | TEST(set.contains("d")); TEST(! com.contains("d")); 68 | TEST(! set.contains("dx")); TEST(com.contains("dx")); 69 | TEST(! set.contains("e")); TEST(com.contains("e")); 70 | TEST(set.contains("ex")); TEST(! com.contains("ex")); 71 | TEST(! set.contains("f")); TEST(com.contains("f")); 72 | TEST(! set.contains("fx")); TEST(com.contains("fx")); 73 | TEST(! set.contains("g")); TEST(com.contains("g")); 74 | 75 | TRY(set.clear()); 76 | TEST(set.empty()); 77 | 78 | TRY((set.insert({"jj","tt"}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[jj,tt]}"); 79 | TRY((set.insert({"tt","xx","()"}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[jj,xx)}"); 80 | TRY((set.erase({"ee","jj","()"}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[jj,xx)}"); 81 | TRY((set.erase({"ee","jj"}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{(jj,xx)}"); 82 | TRY((set.erase({"ll","nn"}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{(jj,ll),(nn,xx)}"); 83 | TRY((set.erase({"pp","rr","()"}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{(jj,ll),(nn,pp],[rr,xx)}"); 84 | TRY((set.insert({"ii","kk"}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[ii,ll),(nn,pp],[rr,xx)}"); 85 | TRY((set.insert({"ww","yy","()"}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[ii,ll),(nn,pp],[rr,yy)}"); 86 | 87 | } 88 | 89 | void test_rs_interval_ordered_set_formatting() { 90 | 91 | Set set; 92 | std::string str; 93 | 94 | TRY((set = {})); TRY(str = std::format("{:?}", set)); TEST_EQUAL(str, R"({})"); 95 | TRY((set = {{"a","b","()"}})); TRY(str = std::format("{:?}", set)); TEST_EQUAL(str, R"({("a","b")})"); 96 | TRY((set = {{"a","b","()"},{"c","d","()"},{"e","f","()"}})); TRY(str = std::format("{:?}", set)); TEST_EQUAL(str, R"({("a","b"),("c","d"),("e","f")})"); 97 | TRY((set = {{"a","b","[]"},{"c","d","[]"},{"e","f","[]"}})); TRY(str = std::format("{:?}", set)); TEST_EQUAL(str, R"({["a","b"],["c","d"],["e","f"]})"); 98 | 99 | } 100 | 101 | void test_rs_interval_ordered_set_operations() { 102 | 103 | using random_int = std::uniform_int_distribution; 104 | 105 | Set set[2], i_set, u_set, d_set, sd_set; 106 | std::vector vec[2]; 107 | Set::iterator it; 108 | Itv in; 109 | std::minstd_rand rng(42); 110 | std::string x; 111 | 112 | static constexpr int iterations = 1000; 113 | static constexpr int max_size = 10; 114 | 115 | for (int i = 0; i < iterations; ++i) { 116 | 117 | for (int j = 0; j < 2; ++j) { 118 | 119 | TRY(set[j].clear()); 120 | vec[j].clear(); 121 | int size = random_int(1, max_size)(rng); 122 | 123 | for (int k = 0; k < size; ++k) { 124 | 125 | auto a = std::string(2, char(random_int('a', 'z')(rng))); 126 | auto b = std::string(2, char(random_int('a', 'z')(rng))); 127 | auto l = Bound(random_int(0, 3)(rng)); 128 | auto r = Bound(random_int(0, 3)(rng)); 129 | 130 | if ((l == Bound::empty) == (r == Bound::empty)) { 131 | TRY(in = Itv(a, b, l, r)); 132 | TRY(set[j].insert(in)); 133 | vec[j].push_back(in); 134 | } 135 | 136 | } 137 | 138 | } 139 | 140 | TRY(i_set = set_intersection(set[0], set[1])); 141 | TRY(u_set = set_union(set[0], set[1])); 142 | TRY(d_set = set_difference(set[0], set[1])); 143 | TRY(sd_set = set_symmetric_difference(set[0], set[1])); 144 | 145 | for (char y = 'a'; y <= 'z'; ++y) { 146 | 147 | for (char z = 'a'; z <= 'z'; ++z) { 148 | 149 | x = {y,z}; 150 | bool member[2]; 151 | 152 | for (int j = 0; j < 2; ++j) { 153 | member[j] = false; 154 | for (const auto& item: vec[j]) { 155 | member[j] |= item(x); 156 | } 157 | } 158 | 159 | auto i_expect = member[0] && member[1]; 160 | auto u_expect = member[0] || member[1]; 161 | auto d_expect = member[0] && ! member[1]; 162 | auto sd_expect = member[0] != member[1]; 163 | auto i_test = false; 164 | auto u_test = false; 165 | auto d_test = false; 166 | auto sd_test = false; 167 | 168 | TRY(i_test = i_set[x]); 169 | TRY(u_test = u_set[x]); 170 | TRY(d_test = d_set[x]); 171 | TRY(sd_test = sd_set[x]); 172 | TEST_EQUAL(i_test, i_expect); 173 | TEST_EQUAL(u_test, u_expect); 174 | TEST_EQUAL(d_test, d_expect); 175 | TEST_EQUAL(sd_test, sd_expect); 176 | 177 | if ((i_test != i_expect) || (u_test != u_expect) || (d_test != d_expect) || (sd_test != sd_expect)) { 178 | for (int j = 0; j < 2; ++j) { 179 | std::println("... {} => {}", vec[j], set[j]); 180 | } 181 | std::println("... x={}", x); 182 | } 183 | 184 | } 185 | 186 | } 187 | 188 | TRY(i_set = set[0]); 189 | TRY(u_set = set[0]); 190 | TRY(d_set = set[0]); 191 | TRY(sd_set = set[0]); 192 | TRY(i_set.apply_intersection(set[1])); 193 | TRY(u_set.apply_union(set[1])); 194 | TRY(d_set.apply_difference(set[1])); 195 | TRY(sd_set.apply_symmetric_difference(set[1])); 196 | 197 | for (char y = 'a'; y <= 'z'; ++y) { 198 | 199 | for (char z = 'a'; z <= 'z'; ++z) { 200 | 201 | x = {y,z}; 202 | bool member[2]; 203 | 204 | for (int j = 0; j < 2; ++j) { 205 | member[j] = false; 206 | for (const auto& item: vec[j]) { 207 | member[j] |= item(x); 208 | } 209 | } 210 | 211 | auto i_expect = member[0] && member[1]; 212 | auto u_expect = member[0] || member[1]; 213 | auto d_expect = member[0] && ! member[1]; 214 | auto sd_expect = member[0] != member[1]; 215 | auto i_test = false; 216 | auto u_test = false; 217 | auto d_test = false; 218 | auto sd_test = false; 219 | 220 | TRY(i_test = i_set[x]); 221 | TRY(u_test = u_set[x]); 222 | TRY(d_test = d_set[x]); 223 | TRY(sd_test = sd_set[x]); 224 | TEST_EQUAL(i_test, i_expect); 225 | TEST_EQUAL(u_test, u_expect); 226 | TEST_EQUAL(d_test, d_expect); 227 | TEST_EQUAL(sd_test, sd_expect); 228 | 229 | if ((i_test != i_expect) || (u_test != u_expect) || (d_test != d_expect) || (sd_test != sd_expect)) { 230 | for (int j = 0; j < 2; ++j) { 231 | std::println("... {} => {}", vec[j], set[j]); 232 | } 233 | std::println("... x={}", x); 234 | } 235 | 236 | } 237 | 238 | } 239 | 240 | } 241 | 242 | } 243 | -------------------------------------------------------------------------------- /src/test/stepwise-boundary-basic-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/interval.hpp" 2 | #include "rs-interval/types.hpp" 3 | #include "test/stepwise-example.hpp" 4 | #include "test/unit-test.hpp" 5 | #include 6 | 7 | using namespace RS::Interval; 8 | using namespace RS::Interval::Detail; 9 | 10 | using Itv = Interval; 11 | using B = Boundary; 12 | using BT = BoundaryType; 13 | 14 | void test_rs_interval_stepwise_boundary_formatting() { 15 | 16 | B b; 17 | 18 | TRY((b = {42, BT::empty})); TEST_EQUAL(std::format("{}", b), "{}"); 19 | TRY((b = {42, BT::minus_infinity})); TEST_EQUAL(std::format("{}", b), "-inf"); 20 | TRY((b = {42, BT::plus_infinity})); TEST_EQUAL(std::format("{}", b), "+inf"); 21 | TRY((b = {42, BT::closed})); TEST_EQUAL(std::format("{}", b), "42"); 22 | 23 | } 24 | 25 | void test_rs_interval_stepwise_boundary_from_interval() { 26 | 27 | static const Itv none; 28 | static const Itv all = Itv::all(); 29 | static const Itv eq1(1); 30 | static const Itv ge1(1, 1, Bound::closed, Bound::unbound); 31 | static const Itv le1(1, 1, Bound::unbound, Bound::closed); 32 | static const Itv c12(1, 2, Bound::closed, Bound::closed); 33 | 34 | TEST_EQUAL(std::format("{}", none), "{}"); 35 | TEST_EQUAL(std::format("{}", all), "*"); 36 | TEST_EQUAL(std::format("{}", eq1), "1"); 37 | TEST_EQUAL(std::format("{}", ge1), ">=1"); 38 | TEST_EQUAL(std::format("{}", le1), "<=1"); 39 | TEST_EQUAL(std::format("{}", c12), "[1,2]"); 40 | 41 | TEST_EQUAL(left_boundary_of(none), (B{0, BT::empty})); 42 | TEST_EQUAL(left_boundary_of(all), (B{0, BT::minus_infinity})); 43 | TEST_EQUAL(left_boundary_of(eq1), (B{1, BT::closed})); 44 | TEST_EQUAL(left_boundary_of(ge1), (B{1, BT::closed})); 45 | TEST_EQUAL(left_boundary_of(le1), (B{0, BT::minus_infinity})); 46 | TEST_EQUAL(left_boundary_of(c12), (B{1, BT::closed})); 47 | 48 | TEST_EQUAL(right_boundary_of(none), (B{0, BT::empty})); 49 | TEST_EQUAL(right_boundary_of(all), (B{0, BT::plus_infinity})); 50 | TEST_EQUAL(right_boundary_of(eq1), (B{1, BT::closed})); 51 | TEST_EQUAL(right_boundary_of(ge1), (B{0, BT::plus_infinity})); 52 | TEST_EQUAL(right_boundary_of(le1), (B{1, BT::closed})); 53 | TEST_EQUAL(right_boundary_of(c12), (B{2, BT::closed})); 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/test/stepwise-boundary-comparison-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/types.hpp" 2 | #include "test/stepwise-example.hpp" 3 | #include "test/unit-test.hpp" 4 | 5 | using namespace RS::Interval; 6 | using namespace RS::Interval::Detail; 7 | 8 | using B = Boundary; 9 | using BT = BoundaryType; 10 | 11 | void test_rs_interval_stepwise_boundary_adjacency() { 12 | 13 | static const B none = {0, BT::empty}; 14 | static const B ninf = {0, BT::minus_infinity}; 15 | static const B pinf = {0, BT::plus_infinity}; 16 | static const B cl1 = {1, BT::closed}; 17 | static const B cl2 = {2, BT::closed}; 18 | 19 | TEST(! none.adjacent(none)); 20 | TEST(! none.adjacent(ninf)); 21 | TEST(! none.adjacent(pinf)); 22 | TEST(! none.adjacent(cl1)); 23 | TEST(! ninf.adjacent(none)); 24 | TEST(! ninf.adjacent(ninf)); 25 | TEST(! ninf.adjacent(pinf)); 26 | TEST(! ninf.adjacent(cl1)); 27 | TEST(! pinf.adjacent(none)); 28 | TEST(! pinf.adjacent(ninf)); 29 | TEST(! pinf.adjacent(pinf)); 30 | TEST(! pinf.adjacent(cl1)); 31 | TEST(! cl1.adjacent(none)); 32 | TEST(! cl1.adjacent(ninf)); 33 | TEST(! cl1.adjacent(pinf)); 34 | TEST(! cl1.adjacent(cl1)); 35 | TEST(cl1.adjacent(cl2)); 36 | TEST(cl2.adjacent(cl1)); 37 | 38 | } 39 | 40 | void test_rs_interval_stepwise_boundary_comparison() { 41 | 42 | static const B none = {0, BT::empty}; 43 | static const B ninf = {0, BT::minus_infinity}; 44 | static const B pinf = {0, BT::plus_infinity}; 45 | static const B cl1 = {1, BT::closed}; 46 | static const B cl2 = {2, BT::closed}; 47 | 48 | TEST(! none.compare_ll(none)); 49 | TEST(none.compare_ll(ninf)); 50 | TEST(none.compare_ll(pinf)); 51 | TEST(none.compare_ll(cl1)); 52 | TEST(! ninf.compare_ll(none)); 53 | TEST(! ninf.compare_ll(ninf)); 54 | TEST(ninf.compare_ll(pinf)); 55 | TEST(ninf.compare_ll(cl1)); 56 | TEST(! pinf.compare_ll(none)); 57 | TEST(! pinf.compare_ll(ninf)); 58 | TEST(! pinf.compare_ll(pinf)); 59 | TEST(! pinf.compare_ll(cl1)); 60 | TEST(! cl1.compare_ll(none)); 61 | TEST(! cl1.compare_ll(ninf)); 62 | TEST(cl1.compare_ll(pinf)); 63 | TEST(! cl1.compare_ll(cl1)); 64 | TEST(cl1.compare_ll(cl2)); 65 | TEST(! cl2.compare_ll(cl1)); 66 | 67 | TEST(! none.compare_rr(none)); 68 | TEST(none.compare_rr(ninf)); 69 | TEST(none.compare_rr(pinf)); 70 | TEST(none.compare_rr(cl1)); 71 | TEST(! ninf.compare_rr(none)); 72 | TEST(! ninf.compare_rr(ninf)); 73 | TEST(ninf.compare_rr(pinf)); 74 | TEST(ninf.compare_rr(cl1)); 75 | TEST(! pinf.compare_rr(none)); 76 | TEST(! pinf.compare_rr(ninf)); 77 | TEST(! pinf.compare_rr(pinf)); 78 | TEST(! pinf.compare_rr(cl1)); 79 | TEST(! cl1.compare_rr(none)); 80 | TEST(! cl1.compare_rr(ninf)); 81 | TEST(cl1.compare_rr(pinf)); 82 | TEST(! cl1.compare_rr(cl1)); 83 | TEST(cl1.compare_rr(cl2)); 84 | TEST(! cl2.compare_rr(cl1)); 85 | 86 | TEST(! none.compare_lr(none)); 87 | TEST(none.compare_lr(ninf)); 88 | TEST(none.compare_lr(pinf)); 89 | TEST(none.compare_lr(cl1)); 90 | TEST(! ninf.compare_lr(none)); 91 | TEST(! ninf.compare_lr(ninf)); 92 | TEST(ninf.compare_lr(pinf)); 93 | TEST(ninf.compare_lr(cl1)); 94 | TEST(! pinf.compare_lr(none)); 95 | TEST(! pinf.compare_lr(ninf)); 96 | TEST(! pinf.compare_lr(pinf)); 97 | TEST(! pinf.compare_lr(cl1)); 98 | TEST(! cl1.compare_lr(none)); 99 | TEST(! cl1.compare_lr(ninf)); 100 | TEST(cl1.compare_lr(pinf)); 101 | TEST(! cl1.compare_lr(cl1)); 102 | TEST(cl1.compare_lr(cl2)); 103 | TEST(! cl2.compare_lr(cl1)); 104 | 105 | TEST(! none.compare_rl(none)); 106 | TEST(none.compare_rl(ninf)); 107 | TEST(none.compare_rl(pinf)); 108 | TEST(none.compare_rl(cl1)); 109 | TEST(! ninf.compare_rl(none)); 110 | TEST(! ninf.compare_rl(ninf)); 111 | TEST(ninf.compare_rl(pinf)); 112 | TEST(ninf.compare_rl(cl1)); 113 | TEST(! pinf.compare_rl(none)); 114 | TEST(! pinf.compare_rl(ninf)); 115 | TEST(! pinf.compare_rl(pinf)); 116 | TEST(! pinf.compare_rl(cl1)); 117 | TEST(! cl1.compare_rl(none)); 118 | TEST(! cl1.compare_rl(ninf)); 119 | TEST(cl1.compare_rl(pinf)); 120 | TEST(! cl1.compare_rl(cl1)); 121 | TEST(cl1.compare_rl(cl2)); 122 | TEST(! cl2.compare_rl(cl1)); 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/test/stepwise-example.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class StepwiseType { 10 | 11 | public: 12 | 13 | constexpr StepwiseType(int n = 0) noexcept: value_(n) {} 14 | constexpr int get() const noexcept { return value_; } 15 | constexpr StepwiseType& operator++() noexcept { ++value_; return *this; } 16 | constexpr StepwiseType operator++(int) noexcept { auto old = *this; ++value_; return old; } 17 | constexpr StepwiseType& operator--() noexcept { --value_; return *this; } 18 | constexpr StepwiseType operator--(int) noexcept { auto old = *this; --value_; return old; } 19 | constexpr bool operator==(StepwiseType s) const noexcept { return value_ == s.value_; } 20 | constexpr bool operator!=(StepwiseType s) const noexcept { return value_ != s.value_; } 21 | constexpr bool operator<(StepwiseType s) const noexcept { return value_ < s.value_; } 22 | constexpr bool operator>(StepwiseType s) const noexcept { return value_ > s.value_; } 23 | constexpr bool operator<=(StepwiseType s) const noexcept { return value_ <= s.value_; } 24 | constexpr bool operator>=(StepwiseType s) const noexcept { return value_ >= s.value_; } 25 | 26 | private: 27 | 28 | int value_; 29 | 30 | }; 31 | 32 | constexpr StepwiseType operator""_sw(unsigned long long n) noexcept { 33 | return StepwiseType(int(n)); 34 | } 35 | 36 | inline std::istream& operator>>(std::istream& in, StepwiseType& s) { 37 | int i; 38 | in >> i; 39 | s = i; 40 | return in; 41 | } 42 | 43 | template <> 44 | struct std::formatter: 45 | std::formatter { 46 | using base = std::formatter; 47 | constexpr auto parse(std::format_parse_context& ctx) { 48 | return ctx.begin(); 49 | } 50 | template 51 | auto format(const StepwiseType& t, FormatContext& ctx) const { 52 | return base::format(t.get(), ctx); 53 | } 54 | }; 55 | 56 | template <> 57 | struct std::hash { 58 | public: 59 | std::size_t operator()(StepwiseType in) const noexcept { 60 | return std::hash()(in.get()); 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /src/test/stepwise-map-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/interval.hpp" 2 | #include "rs-interval/map.hpp" 3 | #include "rs-interval/set.hpp" 4 | #include "rs-interval/types.hpp" 5 | #include "test/stepwise-example.hpp" 6 | #include "test/unit-test.hpp" 7 | #include 8 | #include 9 | 10 | using namespace RS::Interval; 11 | 12 | using Itv = Interval; 13 | using Map = IntervalMap; 14 | 15 | void test_rs_interval_stepwise_map() { 16 | 17 | Map map; 18 | Map::iterator it; 19 | 20 | TEST(map.empty()); 21 | TEST_EQUAL(map.size(), 0u); 22 | TEST_EQUAL(map.default_value(), ""); 23 | TEST_EQUAL(map[42_sw], ""); 24 | TEST_EQUAL(std::format("{}", map), "{}"); 25 | 26 | TRY(map.insert(Itv(2_sw,2_sw,"<="), "alpha")); 27 | TRY(map.insert(Itv(3_sw,7_sw,"()"), "bravo")); 28 | TRY(map.insert(Itv(8_sw,8_sw,">="), "charlie")); 29 | TEST_EQUAL(map.size(), 3u); 30 | TEST_EQUAL(map[1_sw], "alpha"); 31 | TEST_EQUAL(map[2_sw], "alpha"); 32 | TEST_EQUAL(map[3_sw], ""); 33 | TEST_EQUAL(map[4_sw], "bravo"); 34 | TEST_EQUAL(map[5_sw], "bravo"); 35 | TEST_EQUAL(map[6_sw], "bravo"); 36 | TEST_EQUAL(map[7_sw], ""); 37 | TEST_EQUAL(map[8_sw], "charlie"); 38 | TEST_EQUAL(map[9_sw], "charlie"); 39 | TRY(map.default_value("nil")); 40 | TEST_EQUAL(map[1_sw], "alpha"); 41 | TEST_EQUAL(map[2_sw], "alpha"); 42 | TEST_EQUAL(map[3_sw], "nil"); 43 | TEST_EQUAL(map[4_sw], "bravo"); 44 | TEST_EQUAL(map[5_sw], "bravo"); 45 | TEST_EQUAL(map[6_sw], "bravo"); 46 | TEST_EQUAL(map[7_sw], "nil"); 47 | TEST_EQUAL(map[8_sw], "charlie"); 48 | TEST_EQUAL(map[9_sw], "charlie"); 49 | TEST_EQUAL(std::format("{}", map), "{<=2:alpha,[4,6]:bravo,>=8:charlie}"); 50 | 51 | TEST(map.contains(1_sw)); 52 | TEST(map.contains(2_sw)); 53 | TEST(! map.contains(3_sw)); 54 | TEST(map.contains(4_sw)); 55 | TEST(map.contains(5_sw)); 56 | TEST(map.contains(6_sw)); 57 | TEST(! map.contains(7_sw)); 58 | TEST(map.contains(8_sw)); 59 | TEST(map.contains(9_sw)); 60 | 61 | TRY(it = map.find(1_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "alpha"); 62 | TRY(it = map.find(2_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "alpha"); 63 | TRY(it = map.find(3_sw)); TEST(it == map.end()); 64 | TRY(it = map.find(4_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 65 | TRY(it = map.find(5_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 66 | TRY(it = map.find(6_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 67 | TRY(it = map.find(7_sw)); TEST(it == map.end()); 68 | TRY(it = map.find(8_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 69 | TRY(it = map.find(9_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 70 | 71 | TRY(it = map.lower_bound(1_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "alpha"); 72 | TRY(it = map.lower_bound(2_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "alpha"); 73 | TRY(it = map.lower_bound(3_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 74 | TRY(it = map.lower_bound(4_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 75 | TRY(it = map.lower_bound(5_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 76 | TRY(it = map.lower_bound(6_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 77 | TRY(it = map.lower_bound(7_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 78 | TRY(it = map.lower_bound(8_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 79 | TRY(it = map.lower_bound(9_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 80 | 81 | TRY(it = map.upper_bound(1_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 82 | TRY(it = map.upper_bound(2_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 83 | TRY(it = map.upper_bound(3_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "bravo"); 84 | TRY(it = map.upper_bound(4_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 85 | TRY(it = map.upper_bound(5_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 86 | TRY(it = map.upper_bound(6_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 87 | TRY(it = map.upper_bound(7_sw)); REQUIRE(it != map.end()); TEST_EQUAL(it->second, "charlie"); 88 | TRY(it = map.upper_bound(8_sw)); TEST(it == map.end()); 89 | TRY(it = map.upper_bound(9_sw)); TEST(it == map.end()); 90 | 91 | TRY(map.clear()); 92 | TEST(map.empty()); 93 | TRY((map = { 94 | {Itv(20_sw,20_sw,"<="), "alpha"}, 95 | {Itv(30_sw,70_sw,"()"), "bravo"}, 96 | {Itv(80_sw,80_sw,">="), "charlie"}, 97 | })); 98 | TEST_EQUAL(map.size(), 3u); 99 | TEST_EQUAL(std::format("{}", map), "{<=20:alpha,[31,69]:bravo,>=80:charlie}"); 100 | 101 | TRY(map.clear()); 102 | TRY(map.default_value("nil")); 103 | TRY(map.insert(Itv(10_sw,10_sw,"<="), "alpha")); 104 | TRY(map.insert(Itv(1_sw,5_sw,"[]"), "bravo")); 105 | TRY(map.insert(Itv(5_sw,6_sw,"[]"), "charlie")); 106 | TRY(map.insert(Itv(2_sw,3_sw,"[]"), "delta")); 107 | TRY(map.insert(Itv(7_sw,8_sw,"[]"), "charlie")); 108 | TEST_EQUAL(map.size(), 6u); 109 | TEST_EQUAL(std::format("{}", map), 110 | "{<=0:alpha," 111 | "1:bravo," 112 | "[2,3]:delta," 113 | "4:bravo," 114 | "[5,8]:charlie," 115 | "[9,10]:alpha}"); 116 | TEST_EQUAL(map[0_sw], "alpha"); 117 | TEST_EQUAL(map[1_sw], "bravo"); 118 | TEST_EQUAL(map[2_sw], "delta"); 119 | TEST_EQUAL(map[3_sw], "delta"); 120 | TEST_EQUAL(map[4_sw], "bravo"); 121 | TEST_EQUAL(map[5_sw], "charlie"); 122 | TEST_EQUAL(map[6_sw], "charlie"); 123 | TEST_EQUAL(map[7_sw], "charlie"); 124 | TEST_EQUAL(map[8_sw], "charlie"); 125 | TEST_EQUAL(map[9_sw], "alpha"); 126 | TEST_EQUAL(map[10_sw], "alpha"); 127 | TEST_EQUAL(map[11_sw], "nil"); 128 | TEST_EQUAL(map[12_sw], "nil"); 129 | 130 | TRY(map.erase(Itv(1_sw,2_sw,"[]"))); 131 | TRY(map.erase(Itv(6_sw,7_sw,"[]"))); 132 | TRY(map.erase(Itv(10_sw,10_sw,">="))); 133 | TEST_EQUAL(map.size(), 6u); 134 | TEST_EQUAL(std::format("{}", map), 135 | "{<=0:alpha," 136 | "3:delta," 137 | "4:bravo," 138 | "5:charlie," 139 | "8:charlie," 140 | "9:alpha}"); 141 | TEST_EQUAL(map[0_sw], "alpha"); 142 | TEST_EQUAL(map[1_sw], "nil"); 143 | TEST_EQUAL(map[2_sw], "nil"); 144 | TEST_EQUAL(map[3_sw], "delta"); 145 | TEST_EQUAL(map[4_sw], "bravo"); 146 | TEST_EQUAL(map[5_sw], "charlie"); 147 | TEST_EQUAL(map[6_sw], "nil"); 148 | TEST_EQUAL(map[7_sw], "nil"); 149 | TEST_EQUAL(map[8_sw], "charlie"); 150 | TEST_EQUAL(map[9_sw], "alpha"); 151 | TEST_EQUAL(map[10_sw], "nil"); 152 | TEST_EQUAL(map[11_sw], "nil"); 153 | TEST_EQUAL(map[12_sw], "nil"); 154 | 155 | } 156 | -------------------------------------------------------------------------------- /src/test/stepwise-set-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/interval.hpp" 2 | #include "rs-interval/set.hpp" 3 | #include "rs-interval/types.hpp" 4 | #include "test/stepwise-example.hpp" 5 | #include "test/unit-test.hpp" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace RS::Interval; 13 | 14 | using Itv = Interval; 15 | using Set = IntervalSet; 16 | 17 | void test_rs_interval_stepwise_set_construct_insert_erase() { 18 | 19 | Set set, com; 20 | Set::iterator it; 21 | Itv in; 22 | std::string str; 23 | 24 | TEST(set.empty()); 25 | TRY(set = 42_sw); 26 | TEST_EQUAL(set.size(), 1u); 27 | TRY(it = set.begin()); 28 | TEST_EQUAL(std::format("{}", *it), "42"); 29 | TRY(++it); 30 | TEST(it == set.end()); 31 | TRY((in = {5_sw,10_sw})); 32 | TRY(set = in); 33 | TEST_EQUAL(set.size(), 1u); 34 | TRY(it = set.begin()); 35 | TEST_EQUAL(std::format("{}", *it), "[5,10]"); 36 | TRY(++it); 37 | TEST(it == set.end()); 38 | TRY((set = {{5_sw,10_sw},{15_sw,20_sw},{25_sw,30_sw}})); 39 | TRY(it = set.begin()); 40 | TEST_EQUAL(std::format("{}", *it), "[5,10]"); 41 | TRY(++it); 42 | TEST_EQUAL(std::format("{}", *it), "[15,20]"); 43 | TRY(++it); 44 | TEST_EQUAL(std::format("{}", *it), "[25,30]"); 45 | TRY(++it); 46 | TEST(it == set.end()); 47 | 48 | TRY((set = {})); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{}"); 49 | TRY((set = {{3_sw,6_sw,"()"}})); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[4,5]}"); 50 | TRY((set = {{0_sw,5_sw,"()"},{10_sw,15_sw,"()"},{20_sw,25_sw,"()"}})); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[1,4],[11,14],[21,24]}"); 51 | 52 | TRY((set = {{3_sw,6_sw},{9_sw,12_sw},{15_sw,18_sw}})); 53 | TRY(com = set.complement()); 54 | TRY(str = std::format("{}", set)); 55 | TEST_EQUAL(str, "{[3,6],[9,12],[15,18]}"); 56 | TRY(str = std::format("{}", com)); 57 | TEST_EQUAL(str, "{<=2,[7,8],[13,14],>=19}"); 58 | 59 | TEST(! set.contains(1_sw)); TEST(com.contains(1_sw)); 60 | TEST(! set.contains(2_sw)); TEST(com.contains(2_sw)); 61 | TEST(set.contains(3_sw)); TEST(! com.contains(3_sw)); 62 | TEST(set.contains(4_sw)); TEST(! com.contains(4_sw)); 63 | TEST(set.contains(5_sw)); TEST(! com.contains(5_sw)); 64 | TEST(set.contains(6_sw)); TEST(! com.contains(6_sw)); 65 | TEST(! set.contains(7_sw)); TEST(com.contains(7_sw)); 66 | TEST(! set.contains(8_sw)); TEST(com.contains(8_sw)); 67 | TEST(set.contains(9_sw)); TEST(! com.contains(9_sw)); 68 | TEST(set.contains(10_sw)); TEST(! com.contains(10_sw)); 69 | TEST(set.contains(11_sw)); TEST(! com.contains(11_sw)); 70 | TEST(set.contains(12_sw)); TEST(! com.contains(12_sw)); 71 | TEST(! set.contains(13_sw)); TEST(com.contains(13_sw)); 72 | TEST(! set.contains(14_sw)); TEST(com.contains(14_sw)); 73 | TEST(set.contains(15_sw)); TEST(! com.contains(15_sw)); 74 | TEST(set.contains(16_sw)); TEST(! com.contains(16_sw)); 75 | TEST(set.contains(17_sw)); TEST(! com.contains(17_sw)); 76 | TEST(set.contains(18_sw)); TEST(! com.contains(18_sw)); 77 | TEST(! set.contains(19_sw)); TEST(com.contains(19_sw)); 78 | TEST(! set.contains(20_sw)); TEST(com.contains(20_sw)); 79 | 80 | TRY(set.clear()); 81 | TEST(set.empty()); 82 | 83 | TRY((set.insert({10_sw,20_sw}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[10,20]}"); 84 | TRY((set.insert({20_sw,30_sw,"()"}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[10,29]}"); 85 | TRY((set.erase({5_sw,10_sw,"()"}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[10,29]}"); 86 | TRY((set.erase({5_sw,10_sw}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[11,29]}"); 87 | TRY((set.erase({12_sw,14_sw}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{11,[15,29]}"); 88 | TRY((set.erase({16_sw,18_sw,"()"}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{11,[15,16],[18,29]}"); 89 | TRY((set.insert({9_sw,11_sw}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[9,11],[15,16],[18,29]}"); 90 | TRY((set.insert({29_sw,31_sw,"()"}))); TRY(str = std::format("{}", set)); TEST_EQUAL(str, "{[9,11],[15,16],[18,30]}"); 91 | 92 | } 93 | 94 | void test_rs_interval_stepwise_set_operations() { 95 | 96 | using random_int = std::uniform_int_distribution; 97 | 98 | Set set[2], i_set, u_set, d_set, sd_set; 99 | std::vector vec[2]; 100 | Set::iterator it; 101 | Itv in; 102 | std::minstd_rand rng(42); 103 | 104 | static constexpr int iterations = 1000; 105 | static constexpr int max_size = 10; 106 | static constexpr int max_value = 50; 107 | 108 | for (int i = 0; i < iterations; ++i) { 109 | 110 | for (int j = 0; j < 2; ++j) { 111 | 112 | TRY(set[j].clear()); 113 | vec[j].clear(); 114 | int size = random_int(1, max_size)(rng); 115 | 116 | for (int k = 0; k < size; ++k) { 117 | 118 | StepwiseType a = random_int(1, max_value)(rng); 119 | StepwiseType b = random_int(1, max_value)(rng); 120 | auto l = Bound(random_int(0, 3)(rng)); 121 | auto r = Bound(random_int(0, 3)(rng)); 122 | 123 | if ((l == Bound::empty) == (r == Bound::empty)) { 124 | TRY(in = Itv(a, b, l, r)); 125 | TRY(set[j].insert(in)); 126 | vec[j].push_back(in); 127 | } 128 | 129 | } 130 | 131 | } 132 | 133 | TRY(i_set = set_intersection(set[0], set[1])); 134 | TRY(u_set = set_union(set[0], set[1])); 135 | TRY(d_set = set_difference(set[0], set[1])); 136 | TRY(sd_set = set_symmetric_difference(set[0], set[1])); 137 | 138 | for (int x = 0; x <= max_value + 1; ++x) { 139 | 140 | bool member[2]; 141 | 142 | for (int j = 0; j < 2; ++j) { 143 | member[j] = false; 144 | for (const auto& item: vec[j]) { 145 | member[j] |= item(x); 146 | } 147 | } 148 | 149 | auto i_expect = member[0] && member[1]; 150 | auto u_expect = member[0] || member[1]; 151 | auto d_expect = member[0] && ! member[1]; 152 | auto sd_expect = member[0] != member[1]; 153 | auto i_test = false; 154 | auto u_test = false; 155 | auto d_test = false; 156 | auto sd_test = false; 157 | StepwiseType sx {x}; 158 | 159 | TRY(i_test = i_set[sx]); 160 | TRY(u_test = u_set[sx]); 161 | TRY(d_test = d_set[sx]); 162 | TRY(sd_test = sd_set[sx]); 163 | TEST_EQUAL(i_test, i_expect); 164 | TEST_EQUAL(u_test, u_expect); 165 | TEST_EQUAL(d_test, d_expect); 166 | TEST_EQUAL(sd_test, sd_expect); 167 | 168 | if ((i_test != i_expect) || (u_test != u_expect) || (d_test != d_expect) || (sd_test != sd_expect)) { 169 | for (int j = 0; j < 2; ++j) { 170 | std::println("... {} => {}", vec[j], set[j]); 171 | } 172 | std::println("... x={}", x); 173 | } 174 | 175 | } 176 | 177 | TRY(i_set = set[0]); 178 | TRY(u_set = set[0]); 179 | TRY(d_set = set[0]); 180 | TRY(sd_set = set[0]); 181 | TRY(i_set.apply_intersection(set[1])); 182 | TRY(u_set.apply_union(set[1])); 183 | TRY(d_set.apply_difference(set[1])); 184 | TRY(sd_set.apply_symmetric_difference(set[1])); 185 | 186 | for (int x = 0; x <= max_value + 1; ++x) { 187 | 188 | bool member[2]; 189 | 190 | for (int j = 0; j < 2; ++j) { 191 | member[j] = false; 192 | for (const auto& item: vec[j]) { 193 | member[j] |= item(x); 194 | } 195 | } 196 | 197 | auto i_expect = member[0] && member[1]; 198 | auto u_expect = member[0] || member[1]; 199 | auto d_expect = member[0] && ! member[1]; 200 | auto sd_expect = member[0] != member[1]; 201 | auto i_test = false; 202 | auto u_test = false; 203 | auto d_test = false; 204 | auto sd_test = false; 205 | StepwiseType sx {x}; 206 | 207 | TRY(i_test = i_set[sx]); 208 | TRY(u_test = u_set[sx]); 209 | TRY(d_test = d_set[sx]); 210 | TRY(sd_test = sd_set[sx]); 211 | TEST_EQUAL(i_test, i_expect); 212 | TEST_EQUAL(u_test, u_expect); 213 | TEST_EQUAL(d_test, d_expect); 214 | TEST_EQUAL(sd_test, sd_expect); 215 | 216 | if ((i_test != i_expect) || (u_test != u_expect) || (d_test != d_expect) || (sd_test != sd_expect)) { 217 | for (int j = 0; j < 2; ++j) { 218 | std::println("... {} => {}", vec[j], set[j]); 219 | } 220 | std::println("... x={}", x); 221 | } 222 | 223 | } 224 | 225 | } 226 | 227 | } 228 | -------------------------------------------------------------------------------- /src/test/types-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/types.hpp" 2 | #include "test/stepwise-example.hpp" 3 | #include "test/unit-test.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace RS::Interval; 12 | using namespace std::literals; 13 | 14 | struct Thing { 15 | std::string name; 16 | Thing() = default; 17 | explicit Thing(const std::string& s): name(s) {} 18 | }; 19 | 20 | void test_rs_interval_types_traits() { 21 | 22 | TEST_EQUAL(interval_category, Category::none); 23 | TEST_EQUAL(interval_category, Category::none); 24 | TEST_EQUAL(interval_category, Category::none); 25 | TEST_EQUAL(interval_category, Category::none); 26 | TEST_EQUAL(interval_category, Category::none); 27 | TEST_EQUAL(interval_category>, Category::none); 28 | 29 | TEST_EQUAL(interval_category, Category::ordered); 30 | TEST_EQUAL(interval_category, Category::ordered); 31 | TEST_EQUAL(interval_category, Category::ordered); 32 | TEST_EQUAL(interval_category, Category::ordered); 33 | TEST_EQUAL(interval_category>, Category::ordered); 34 | TEST_EQUAL(interval_category, Category::ordered); 35 | TEST_EQUAL(interval_category, Category::ordered); 36 | 37 | TEST_EQUAL(interval_category, Category::stepwise); 38 | TEST_EQUAL(interval_category, Category::stepwise); 39 | TEST_EQUAL(interval_category, Category::stepwise); 40 | TEST_EQUAL(interval_category, Category::stepwise); 41 | TEST_EQUAL(interval_category::const_iterator>, Category::stepwise); 42 | TEST_EQUAL(interval_category::iterator>, Category::stepwise); 43 | TEST_EQUAL(interval_category, Category::stepwise); 44 | TEST_EQUAL(interval_category, Category::stepwise); 45 | 46 | TEST_EQUAL(interval_category, Category::integral); 47 | TEST_EQUAL(interval_category, Category::integral); 48 | TEST_EQUAL(interval_category, Category::integral); 49 | TEST_EQUAL(interval_category, Category::integral); 50 | TEST_EQUAL(interval_category, Category::integral); 51 | TEST_EQUAL(interval_category, Category::integral); 52 | 53 | TEST_EQUAL(interval_category, Category::continuous); 54 | TEST_EQUAL(interval_category, Category::continuous); 55 | TEST_EQUAL(interval_category, Category::continuous); 56 | TEST_EQUAL(interval_category, Category::continuous); 57 | TEST_EQUAL(interval_category, Category::continuous); 58 | 59 | } 60 | 61 | void test_rs_interval_types_concepts() { 62 | 63 | TEST(Ordered); 64 | TEST(Ordered); 65 | TEST(Ordered); 66 | TEST(Ordered); 67 | TEST(Ordered>); 68 | TEST(Ordered); 69 | TEST(Ordered); 70 | TEST(! Ordered); 71 | TEST(! Ordered); 72 | TEST(! Ordered); 73 | TEST(! Ordered); 74 | TEST(! Ordered::const_iterator>); 75 | TEST(! Ordered::iterator>); 76 | TEST(! Ordered); 77 | TEST(! Ordered); 78 | TEST(! Ordered); 79 | TEST(! Ordered); 80 | TEST(! Ordered); 81 | TEST(! Ordered); 82 | TEST(! Ordered); 83 | TEST(! Ordered); 84 | TEST(! Ordered); 85 | TEST(! Ordered); 86 | TEST(! Ordered); 87 | TEST(! Ordered); 88 | TEST(! Ordered); 89 | TEST(! Ordered); 90 | TEST(! Ordered); 91 | TEST(! Ordered); 92 | TEST(! Ordered); 93 | TEST(! Ordered); 94 | TEST(! Ordered>); 95 | 96 | TEST(! Stepwise); 97 | TEST(! Stepwise); 98 | TEST(! Stepwise); 99 | TEST(! Stepwise); 100 | TEST(! Stepwise>); 101 | TEST(! Stepwise); 102 | TEST(! Stepwise); 103 | TEST(Stepwise); 104 | TEST(Stepwise); 105 | TEST(Stepwise); 106 | TEST(Stepwise); 107 | TEST(Stepwise::const_iterator>); 108 | TEST(Stepwise::iterator>); 109 | TEST(Stepwise); 110 | TEST(Stepwise); 111 | TEST(! Stepwise); 112 | TEST(! Stepwise); 113 | TEST(! Stepwise); 114 | TEST(! Stepwise); 115 | TEST(! Stepwise); 116 | TEST(! Stepwise); 117 | TEST(! Stepwise); 118 | TEST(! Stepwise); 119 | TEST(! Stepwise); 120 | TEST(! Stepwise); 121 | TEST(! Stepwise); 122 | TEST(! Stepwise); 123 | TEST(! Stepwise); 124 | TEST(! Stepwise); 125 | TEST(! Stepwise); 126 | TEST(! Stepwise); 127 | TEST(! Stepwise>); 128 | 129 | TEST(! Integral); 130 | TEST(! Integral); 131 | TEST(! Integral); 132 | TEST(! Integral); 133 | TEST(! Integral>); 134 | TEST(! Integral); 135 | TEST(! Integral); 136 | TEST(! Integral); 137 | TEST(! Integral); 138 | TEST(! Integral); 139 | TEST(! Integral); 140 | TEST(! Integral::const_iterator>); 141 | TEST(! Integral::iterator>); 142 | TEST(! Integral); 143 | TEST(! Integral); 144 | TEST(Integral); 145 | TEST(Integral); 146 | TEST(Integral); 147 | TEST(Integral); 148 | TEST(Integral); 149 | TEST(Integral); 150 | TEST(! Integral); 151 | TEST(! Integral); 152 | TEST(! Integral); 153 | TEST(! Integral); 154 | TEST(! Integral); 155 | TEST(! Integral); 156 | TEST(! Integral); 157 | TEST(! Integral); 158 | TEST(! Integral); 159 | TEST(! Integral); 160 | TEST(! Integral>); 161 | 162 | TEST(! Continuous); 163 | TEST(! Continuous); 164 | TEST(! Continuous); 165 | TEST(! Continuous); 166 | TEST(! Continuous>); 167 | TEST(! Continuous); 168 | TEST(! Continuous); 169 | TEST(! Continuous); 170 | TEST(! Continuous); 171 | TEST(! Continuous); 172 | TEST(! Continuous); 173 | TEST(! Continuous::const_iterator>); 174 | TEST(! Continuous::iterator>); 175 | TEST(! Continuous); 176 | TEST(! Continuous); 177 | TEST(! Continuous); 178 | TEST(! Continuous); 179 | TEST(! Continuous); 180 | TEST(! Continuous); 181 | TEST(! Continuous); 182 | TEST(! Continuous); 183 | TEST(Continuous); 184 | TEST(Continuous); 185 | TEST(Continuous); 186 | TEST(Continuous); 187 | TEST(Continuous); 188 | TEST(! Continuous); 189 | TEST(! Continuous); 190 | TEST(! Continuous); 191 | TEST(! Continuous); 192 | TEST(! Continuous); 193 | TEST(! Continuous>); 194 | 195 | TEST(! Arithmetic); 196 | TEST(! Arithmetic); 197 | TEST(! Arithmetic); 198 | TEST(! Arithmetic); 199 | TEST(! Arithmetic>); 200 | TEST(! Arithmetic); 201 | TEST(! Arithmetic); 202 | TEST(! Arithmetic); 203 | TEST(! Arithmetic); 204 | TEST(! Arithmetic); 205 | TEST(! Arithmetic); 206 | TEST(! Arithmetic::const_iterator>); 207 | TEST(! Arithmetic::iterator>); 208 | TEST(! Arithmetic); 209 | TEST(! Arithmetic); 210 | TEST(Arithmetic); 211 | TEST(Arithmetic); 212 | TEST(Arithmetic); 213 | TEST(Arithmetic); 214 | TEST(Arithmetic); 215 | TEST(Arithmetic); 216 | TEST(Arithmetic); 217 | TEST(Arithmetic); 218 | TEST(Arithmetic); 219 | TEST(Arithmetic); 220 | TEST(Arithmetic); 221 | TEST(! Arithmetic); 222 | TEST(! Arithmetic); 223 | TEST(! Arithmetic); 224 | TEST(! Arithmetic); 225 | TEST(! Arithmetic); 226 | TEST(! Arithmetic>); 227 | 228 | TEST(IntervalCompatible); 229 | TEST(IntervalCompatible); 230 | TEST(IntervalCompatible); 231 | TEST(IntervalCompatible); 232 | TEST(IntervalCompatible>); 233 | TEST(IntervalCompatible); 234 | TEST(IntervalCompatible); 235 | TEST(IntervalCompatible); 236 | TEST(IntervalCompatible); 237 | TEST(IntervalCompatible); 238 | TEST(IntervalCompatible); 239 | TEST(IntervalCompatible::const_iterator>); 240 | TEST(IntervalCompatible::iterator>); 241 | TEST(IntervalCompatible); 242 | TEST(IntervalCompatible); 243 | TEST(IntervalCompatible); 244 | TEST(IntervalCompatible); 245 | TEST(IntervalCompatible); 246 | TEST(IntervalCompatible); 247 | TEST(IntervalCompatible); 248 | TEST(IntervalCompatible); 249 | TEST(IntervalCompatible); 250 | TEST(IntervalCompatible); 251 | TEST(IntervalCompatible); 252 | TEST(IntervalCompatible); 253 | TEST(IntervalCompatible); 254 | TEST(! IntervalCompatible); 255 | TEST(! IntervalCompatible); 256 | TEST(! IntervalCompatible); 257 | TEST(! IntervalCompatible); 258 | TEST(! IntervalCompatible); 259 | TEST(! IntervalCompatible>); 260 | 261 | TEST(! Scalar); 262 | TEST(! Scalar); 263 | TEST(! Scalar); 264 | TEST(! Scalar); 265 | TEST(! Scalar>); 266 | TEST(! Scalar); 267 | TEST(! Scalar); 268 | TEST(! Scalar); 269 | TEST(! Scalar); 270 | TEST(! Scalar); 271 | TEST(! Scalar); 272 | TEST(! Scalar::const_iterator>); 273 | TEST(! Scalar::iterator>); 274 | TEST(! Scalar); 275 | TEST(! Scalar); 276 | TEST(! Scalar); 277 | TEST(! Scalar); 278 | TEST(! Scalar); 279 | TEST(! Scalar); 280 | TEST(! Scalar); 281 | TEST(! Scalar); 282 | TEST(Scalar); 283 | TEST(Scalar); 284 | TEST(Scalar); 285 | TEST(Scalar); 286 | TEST(Scalar); 287 | TEST(! Scalar); 288 | TEST(! Scalar); 289 | TEST(! Scalar); 290 | TEST(! Scalar); 291 | TEST(! Scalar); 292 | TEST(! Scalar>); 293 | 294 | } 295 | 296 | void test_rs_interval_types_boundary() { 297 | 298 | using BT = Detail::BoundaryType; 299 | using BI = Detail::Boundary; 300 | 301 | BI bi; 302 | std::string str; 303 | 304 | TRY((bi = {})); TRY(str = std::format("{}", bi)); TEST_EQUAL(str, "{}"); 305 | TRY((bi = {{}, BT::empty})); TRY(str = std::format("{}", bi)); TEST_EQUAL(str, "{}"); 306 | TRY((bi = {{}, BT::minus_infinity})); TRY(str = std::format("{}", bi)); TEST_EQUAL(str, "-inf"); 307 | TRY((bi = {{}, BT::plus_infinity})); TRY(str = std::format("{}", bi)); TEST_EQUAL(str, "+inf"); 308 | TRY((bi = {42, BT::closed})); TRY(str = std::format("{}", bi)); TEST_EQUAL(str, "42"); 309 | TRY((bi = {42, BT::open})); TRY(str = std::format("{}", bi)); TEST_EQUAL(str, "(42)"); 310 | 311 | } 312 | 313 | void test_rs_interval_types_from_string() { 314 | 315 | int n = 0; 316 | std::string s; 317 | Thing t; 318 | 319 | s = "Hello"; 320 | TRY(t = Detail::from_string(s)); 321 | TEST_EQUAL(t.name, "Hello"); 322 | 323 | s = "42"; 324 | TRY(n = Detail::from_string(s)); 325 | TEST_EQUAL(n, 42); 326 | 327 | } 328 | -------------------------------------------------------------------------------- /src/test/unit-test.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #ifdef _WIN32 19 | #include 20 | #else 21 | #include 22 | #endif 23 | 24 | namespace RS::UnitTest { 25 | 26 | const inline auto tty = 27 | #ifdef _WIN32 28 | [] { 29 | DWORD mode = 0; 30 | return GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &mode) != 0; 31 | }(); 32 | #else 33 | isatty(1) != 0; 34 | #endif 35 | 36 | // black = esc [30m 37 | // red = esc [31m 38 | // green = esc [32m 39 | // yellow = esc [33m 40 | // blue = esc [34m 41 | // magenta = esc [35m 42 | // cyan = esc [36m 43 | // white = esc [37m 44 | 45 | const inline std::string_view xreset = tty ? "\x1b[0m" : ""; // reset colours 46 | const inline std::string_view xhead = tty ? "\x1b[33m" : ""; // yellow 47 | const inline std::string_view xrule = tty ? "\x1b[36m" : ""; // cyan 48 | const inline std::string_view xtest = tty ? "\x1b[33m" : ""; // yellow 49 | const inline std::string_view xpass = tty ? "\x1b[32m" : ""; // green 50 | const inline std::string_view xfail = tty ? "\x1b[31m" : ""; // red 51 | 52 | const inline std::string rule(30, '='); 53 | 54 | inline auto failures = 0; 55 | inline std::vector main_args; 56 | 57 | struct TextMatch { 58 | 59 | std::string text; 60 | 61 | bool operator()(std::string pattern) const { 62 | std::regex regex(pattern); 63 | return std::regex_search(text, regex); 64 | } 65 | 66 | }; 67 | 68 | inline void call_me_maybe(void (*test)(), std::string name) { 69 | if (main_args.empty() || std::ranges::any_of(main_args, TextMatch{name})) { 70 | std::println("{}{}{}", xtest, name, xreset); 71 | test(); 72 | } 73 | } 74 | 75 | inline bool read_file_contents(const std::filesystem::path& path, std::string& out) { 76 | 77 | auto file_ptr = std::fopen(path.c_str(), "rb"); 78 | 79 | if (file_ptr == nullptr) { 80 | return false; 81 | } 82 | 83 | std::fseek(file_ptr, 0, SEEK_END); 84 | auto size = static_cast(std::ftell(file_ptr)); 85 | std::fseek(file_ptr, 0, SEEK_SET); 86 | out.resize(size); 87 | std::fread(out.data(), 1, size, file_ptr); 88 | 89 | return true; 90 | 91 | } 92 | 93 | } 94 | 95 | // Explicitly trigger a failure. 96 | 97 | #define FAIL(...) do { \ 98 | std::println("{}... Test failed [{}:{}]{}\n\t{}", \ 99 | ::RS::UnitTest::xfail, __FILE__, __LINE__, ::RS::UnitTest::xreset, std::format(__VA_ARGS__)); \ 100 | ++::RS::UnitTest::failures; \ 101 | } while (false) 102 | 103 | // Evaluate an expression. Fails if any exception is thrown. 104 | 105 | #define TRY(expr) do { \ 106 | try { \ 107 | (expr); \ 108 | } \ 109 | catch (const std::exception& _test_except) { \ 110 | FAIL("Unexpected exception: {}", _test_except.what()); \ 111 | } \ 112 | catch (...) { \ 113 | FAIL("Unexpected exception"); \ 114 | } \ 115 | } while (false) 116 | 117 | // Evaluate a boolean expression. Fails if the expression is false, or if any 118 | // exception is thrown. 119 | 120 | #define TEST(expr) do { \ 121 | try { \ 122 | auto _test_flag = static_cast(expr); \ 123 | if (! _test_flag) { \ 124 | FAIL("Expression is false: {}", # expr); \ 125 | } \ 126 | } \ 127 | catch (const std::exception& _test_except) { \ 128 | FAIL("Unexpected exception: {}", _test_except.what()); \ 129 | } \ 130 | catch (...) { \ 131 | FAIL("Unexpected exception"); \ 132 | } \ 133 | } while (false) 134 | 135 | // Same as TEST(), but quit the current test function on failure. This is 136 | // intended for situations where a failure at this point would render the 137 | // rest of the tests meaningless or impossible. 138 | 139 | #define REQUIRE(expr) do { \ 140 | try { \ 141 | auto _test_flag = static_cast(expr); \ 142 | if (! _test_flag) { \ 143 | FAIL("Expression is false: {}", # expr); \ 144 | return; \ 145 | } \ 146 | } \ 147 | catch (const std::exception& _test_except) { \ 148 | FAIL("Unexpected exception: {}", _test_except.what()); \ 149 | return; \ 150 | } \ 151 | catch (...) { \ 152 | FAIL("Unexpected exception"); \ 153 | return; \ 154 | } \ 155 | } while (false) 156 | 157 | // Compare two expressions for equality. Fails if the expressions are not 158 | // equal, or if any exception is thrown. 159 | 160 | #define TEST_EQUAL(lhs, rhs) do { \ 161 | try { \ 162 | auto _test_lhs {lhs}; \ 163 | auto _test_rhs {rhs}; \ 164 | if (_test_lhs != _test_rhs) { \ 165 | FAIL("Expressions are not equal\n\t{} = {}\n\t{} = {}", \ 166 | # lhs, _test_lhs, # rhs, _test_rhs); \ 167 | } \ 168 | } \ 169 | catch (const std::exception& _test_except) { \ 170 | FAIL("Unexpected exception: {}", _test_except.what()); \ 171 | } \ 172 | catch (...) { \ 173 | FAIL("Unexpected exception"); \ 174 | } \ 175 | } while (false) 176 | 177 | // Compare two floating point expressions. Fails if the expressions differ by 178 | // more than the tolerance in either direction, or if any exception is 179 | // thrown. 180 | 181 | #define TEST_NEAR(lhs, rhs, tolerance) do { \ 182 | try { \ 183 | auto _test_lhs = static_cast(lhs); \ 184 | auto _test_rhs = static_cast(rhs); \ 185 | auto _test_tolerance = static_cast(tolerance); \ 186 | auto _test_delta = std::abs(_test_lhs - _test_rhs); \ 187 | if (_test_delta > _test_tolerance) { \ 188 | FAIL("Difference between expressions is too great\n" \ 189 | "\t{} = {}\n\t{} = {}\n\ttolerance = {}", \ 190 | # lhs, _test_lhs, # rhs, _test_rhs, _test_tolerance); \ 191 | } \ 192 | } \ 193 | catch (const std::exception& ex) { \ 194 | FAIL("Unexpected exception: {}", ex.what()); \ 195 | } \ 196 | catch (...) { \ 197 | FAIL("Unexpected exception"); \ 198 | } \ 199 | } while (false) 200 | 201 | // Check an expression against a range. Fails if the expression is outside the 202 | // range, or if any exception is thrown. 203 | 204 | #define TEST_IN_RANGE(expr, min, max) do { \ 205 | try { \ 206 | auto _test_expr {expr}; \ 207 | auto _test_min {min}; \ 208 | auto _test_max {max}; \ 209 | if (_test_expr < _test_min || _test_expr > _test_max) \ 210 | FAIL("Expression is out of range\n" \ 211 | "\t{} = {}\n\tmin = {}, max = {}", \ 212 | # expr, _test_expr, _test_min, _test_max); \ 213 | } \ 214 | catch (const std::exception& _test_except) { \ 215 | FAIL("Unexpected exception: {}", _test_except.what()); \ 216 | } \ 217 | catch (...) { \ 218 | FAIL("Unexpected exception"); \ 219 | } \ 220 | } while (false) 221 | 222 | // Compare an expression (expected to return a string) for a substring match 223 | // against a regular expression. Fails if the result does not match or an 224 | // exception is thrown. The regex pattern is passed as a string literal, and 225 | // can be made case insensitive by appending "/i". 226 | 227 | #define TEST_MATCH(expr, pattern) do { \ 228 | try { \ 229 | auto _test_string = static_cast(expr); \ 230 | if (! ::RS::UnitTest::TextMatch{_test_string}(pattern)) { \ 231 | FAIL("Expression does not match pattern: {:?}", _test_string); \ 232 | } \ 233 | } \ 234 | catch (const std::exception& _test_except) { \ 235 | FAIL("Unexpected exception: {}", _test_except.what()); \ 236 | } \ 237 | catch (...) { \ 238 | FAIL("Unexpected exception"); \ 239 | } \ 240 | } while (false) 241 | 242 | // Evaluate an expression that is expected to throw an exception of the given 243 | // type, or a type derived from it. Fails if no exception is thrown, if an 244 | // exception of the wrong type is thrown, or if the exception's type is 245 | // correct but its what() message does not match the regular expression. The 246 | // regex pattern is passed as a string literal, and can be made case 247 | // insensitive by appending "/i". 248 | 249 | #define TEST_THROW(expr, exception_type, pattern) do { \ 250 | try { \ 251 | (expr); \ 252 | FAIL("No exception thrown: {}", # expr); \ 253 | } \ 254 | catch (const exception_type& _test_except) { \ 255 | if (! ::RS::UnitTest::TextMatch{_test_except.what()}(pattern)) { \ 256 | FAIL("Unexpected error message: {}", _test_except.what()); \ 257 | } \ 258 | } \ 259 | catch (const std::exception& _test_except) { \ 260 | FAIL("Unexpected exception: {}", _test_except.what()); \ 261 | } \ 262 | catch (...) { \ 263 | FAIL("Unexpected exception"); \ 264 | } \ 265 | } while (false) 266 | -------------------------------------------------------------------------------- /src/test/version-test.cpp: -------------------------------------------------------------------------------- 1 | #include "rs-interval/version.hpp" 2 | #include "test/unit-test.hpp" 3 | 4 | using namespace RS::Interval; 5 | 6 | void test_rs_interval_version() { 7 | 8 | TEST_IN_RANGE(version()[0], 0, 100); 9 | TEST_IN_RANGE(version()[1], 0, 1'000); 10 | TEST_IN_RANGE(version()[2], 0, 10'000); 11 | TEST_MATCH(version_string(), R"(\d+\.\d+\.\d+)"); 12 | 13 | } 14 | --------------------------------------------------------------------------------