Git Product home page Git Product logo

interval-tree's Introduction

interval-tree

A C++ header only interval tree implementation, which takes a red black tree as its base to inhibit degeneration to linked lists.

How an interval tree looks like:

ExampleTree

Example

// #include <interval-tree/draw.hpp> // to draw tree. this is not header only anymore.
#include <interval-tree/interval_tree.hpp>

int main()
{
  using namespace lib_interval_tree;

  // interval_tree<interval<int>>; // closed by default
  // interval_tree<interval<int, open>>;
  // interval_tree<interval<int, closed>>;
  // interval_tree<interval<int, left_open>>;
  // interval_tree<interval<int, right_open>>;
  // interval_tree<interval<int, closed_adjacent>>; // counts adjacent intervals as overlapping
  interval_tree_t<int> tree;

  tree.insert(make_safe_interval<int>(21, 16)); // make_safe_interval swaps low and high if not in right order.
  tree.insert({8, 9});
  tree.insert({25, 30});
  tree.insert({5, 8});
  tree.insert({15, 23});
  tree.insert({17, 19});
  tree.insert({26, 26});
  tree.insert({0, 3});
  tree.insert({6, 10});
  tree.insert({19, 20});

  tree.deoverlap();
  
  for (auto const& i : tree)
  {
    std::cout << "[" << i.low() << ", " << i.high() << "]\n";
  }

  // dynamic has some logic overhead.
  interval_tree<interval<int, dynamic>> dynamicIntervals;
  dynamicIntervals.insert({0, 1, interval_border::closed, interval_border::open});
  dynamicIntervals.insert({7, 5, interval_border::open, interval_border::closed_adjacent});
}

Compile & Run Testing

Having googletest (find here on github) installed / built is a requirement to run the tests. Create a build folder, navigate there, run cmake and build the tree-tests target. You might have to adapt the linker line for gtest, if you built it yourself and didn't install it into your system. If you want to generate the pretty drawings, install cairo, pull the submodule and pass INT_TREE_DRAW_EXAMPLES=on to the cmake command line to generate a drawings/make_drawings executeable.

Free Functions

interval<NumericT, Kind> make_safe_interval(NumericT border1, NumericT border2)

Creates an interval where the borders are sorted so the lower border is the first one.

Members of IntervalTree

iterator insert(interval_type const& ival)

Adds an interval into the tree.

Parameters

  • ival An interval

Returns: An iterator to the inserted element.


iterator insert_overlap(interval_type const& ival)

Inserts an interval into the tree if no other interval overlaps it. Otherwise merge the interval with the one being overlapped.

Parameters

  • ival An interval
  • exclusive Exclude borders from overlap check. Defaults to false.
  • mergeSetOverlapping If the result of interval::join is a collection of intervals, shall each be inserted with more overlap searches? Defaults to false

Returns: An iterator to the inserted element.


iterator erase(iterator iter)

Removes the interval given by iterator from the tree. (does not invalidate iterators).

Parameters

  • iter A valid non-end iterator

Returns: An iterator to the next element.


size_type size() const

Returns the amount of nodes in the tree.

Returns: The amount of tree nodes.


(const)iterator find(interval_type const& ival)

Finds the first interval in the interval tree that has an exact match. WARNING: There is no special handling for floats.

Parameters

  • ival The interval to find.

Returns: An iterator to the found element, or std::end(tree).


(const)iterator find(interval_type const& ival, CompareFunctionT const& compare)

Finds the first interval in the interval tree that has the following statement evaluate to true: compare(interval_in_tree, ival); Allows for propper float comparisons.

Parameters

  • ival The interval to find.
  • compare The compare function to compare intervals with. Function is called like so: compare(interval_in_tree, ival).

Returns: An iterator to the found element, or std::end(tree).


(const)iterator find_all(interval_type const& ival, OnFindFunctionT const& on_find)

Find all intervals in the tree matching ival.

Parameters

  • ival The interval to find.
  • on_find A function of type bool(iterator) that is called when an interval was found. Return true to continue, false to preemptively abort search.

Example

tree.insert({3, 7});
tree.insert({3, 7});
tree.insert({8, 9});
tree.find_all({3, 7}, [](auto iter) /* iter will be const_iterator if tree is const */ {
  // will find all intervals that are exactly {3,7} here.
  return true; // continue
});

Returns: An iterator to the found element, or std::end(tree).


(const)iterator find_all(interval_type const& ival, OnFindFunctionT const& on_find, CompareFunctionT const& compare)

Find all intervals in the tree that the compare function returns true for.

Parameters

  • ival The interval to find.
  • compare The compare function to compare intervals with. Function is called like so: compare(interval_in_tree, ival).
  • on_find A function of type bool(iterator) that is called when an interval was found. Return true to continue, false to preemptively abort search.

Returns: An iterator to the found element, or std::end(tree).


(const)iterator find_next_in_subtree(iterator from, interval_type const& ival)

Finds the next exact match EXCLUDING from in the subtree originating from "from". You cannot find all matches this way, use find_all for that.

Parameters

  • from The iterator to start from. (including this iterator!)
  • ival The interval to find.

Returns: An iterator to the found element, or std::end(tree).


(const)iterator find_next_in_subtree(iterator from, interval_type const& ival, CompareFunctionT const& compare)

Finds the next exact match EXCLUDING from in the subtree originating from "from". You cannot find all matches this way, use find_all for that.

Parameters

  • from The iterator to start from (including this iterator!)
  • ival The interval to find.
  • compare The compare function to compare intervals with. Function is called like so: compare(interval_in_tree, ival).

Returns: An iterator to the found element, or std::end(tree).


(const)iterator overlap_find(interval_type const& ival, bool exclusive)

Finds the first interval in the interval tree that overlaps the given interval.

Parameters

  • ival The interval to find an overlap for.
  • exclusive Exclude borders from overlap check. Defaults to false.

Returns: An iterator to the found element, or std::end(tree).


(const)iterator overlap_find_all(interval_type const& ival, OnFindFunctionT const& on_find, bool exclusive)

Finds all intervals in the interval tree that overlaps the given interval.

Parameters

  • ival The interval to find an overlap for.
  • on_find A function of type bool(iterator) that is called when an interval was found. Return true to continue, false to preemptively abort search.
  • exclusive Exclude borders from overlap check. Defaults to false.

Example

tree.insert({0, 5});
tree.insert({5, 10});
tree.insert({10, 15});
tree.overlap_find_all({5, 5}, [](auto iter) /* iter will be const_iterator if tree is const */ {
  // called with {0, 5} and {5, 10} in unspecified order.
  return true; // continue
});

Returns: An iterator to the found element, or std::end(tree).


(const)iterator overlap_find_next_in_subtree(interval_type const& ival, bool exclusive)

Finds the next interval in the subtree originating in ival that overlaps the given interval. You cannot find all matches this way, use overlap_find_all for that.

Parameters

  • ival The interval to find an overlap for.
  • exclusive Exclude borders from overlap check. Defaults to false.

Returns: An iterator to the found element, or std::end(tree).


interval_tree& deoverlap()

Merges all overlapping intervals within the tree. After calling deoverlap, the tree will only contain disjoint intervals.

Returns: *this

After deoverlap

AfterDeoverlap

interval_tree deoverlap_copy()

Same as deoverlap, but not inplace


interval_tree punch(interval_type const& ival)

Removes all intervals from ival and produces a tree that contains the remaining intervals. The tree must be deoverlapped, or the result is undefined. ival is expected to encompass the entire interval range.

Returns: A new interval_tree containing the gaps.

After punching (with [0, 50])

AfterPunch


interval_tree punch()

Same as punch(interval_type const& ival), but with ival = [lowest_lower_bound, highest_upper_bound], resulting in only the gaps between existing intervals.


bool empty() const noexcept

Returns whether or not the tree is empty.

Returns: Is this tree empty?


iterator begin()

Returns the iterator of the interval with the lowest lower_bound.

Returns: begin iterator.


iterator end()

Returns a past the end iterator.

Returns: past the end iterator.


iterator cbegin()

Returns the const_iterator of the interval with the lowest lower_bound.

Returns: begin iterator.


iterator cend()

Returns a past the end const_iterator.

Returns: past the end const_iterator.


reverse_iterator rbegin()

Returns the iterator of the interval with the highest lower_bound.

Returns: rbegin iterator.


reverse_iterator rend()

Returns a past the end iterator in reverse.

Returns: past the end iterator.


reverse_iterator crbegin()

Returns the const_iterator of the interval with the highest lower_bound.

Returns: begin iterator.


reverse_iterator crend()

Returns a past the end const_iterator in reverse.

Returns: past the end const_iterator.

Members of Interval

You can implement your own interval if you provide the same functions, except (within, operator-, size, operator!=).

There are 6 types of intervals:

  • open: (a, b)
  • left_open: (a, b]
  • right_open: [a, b)
  • closed: [a, b]
  • closed_adjacent: [a, b] (counts adjacent intervals as overlapping)
  • dynamic: Can be any of the above, depending on the input. Not supported for floating point.

Which can be picked with the second template parameter of interval: lib_interval_tree::interval<int, lib_interval_tree::open>

using value_type

The underlying interval numerical type

using interval_kind

The interval kind. You dont need to provides this typedef in your interval class.

friend bool operator==(interval const& lhs, interval const& other)

Comparison operator.

friend bool operator!=(interval const& lhs, interval const& other)

Comparison operator.

value_type low() const

Lower bound.

value_type high() const

Upper bound.

[[deprecated]] bool overlaps(value_type l, value_type h) const

Overlap these bounds with this interval? Is deprecated because the overlapping does not work with the dynamic interval type.

bool overlaps_exclusive(value_type l, value_type h) const

Overlap these bounds with this interval excluding borders?

bool overlaps(interval const& other) const

Like overlaps with lower and upper bound.

bool overlaps_exclusive(interval const& other) const

Like overlaps with lower and upper bound.

bool within(value_type value) const

Is the value within the interval?

bool within(interval const& other) const

Is the interval within the interval?

value_type operator-(interval const& other) const

Calculates the distance between the two intervals. Overlapping intervals have 0 distance.

value_type size() const

Returns The amount of elements in the interval when integral, or the distance between the 2 bounds when floating point.

interval join(interval const& other) const

Joins 2 intervals and whatever is inbetween.

interval-tree's People

Contributors

5cript avatar maximsmolskiy avatar webernick avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

interval-tree's Issues

Unlicense is invalid in some jurisdictions

Hey there,

it looks like you've created a wonderful library, which I'd love to use for a project. Unfortunately, the Unlicense is legally void in some jurisdictions, such as Germany, where I'm from. The core problem is that under German law you, the author of a work, have certain rights which you cannot relinquish under any circumstance, hence you cannot fully put a work in the public domain. It may or may not be problematic in other jurisdictions, see this thread on StackExchange. However, since I'm not familiar with other legal systems, I can't comment on the validity of other remarks.

Since you released your code under the Unlicense, I assume your intent is for it to useful to as many people as possible. In that regard, may I recommend to you to either dual license under the Unlicense and another relatively free license such as the MIT license, or to maybe switch to CC0, which has much of the same intent, but handles varying legal systems better.

Thank you for your consideration.

overlap_find_all does not agree with manual search

I'm having some difficulty with the search for all overlapping intervals. My use case is a bunch of overlapping latitudinal intervals, where I'd like to find all intervals that overlap with a specified interval.

Contents of interval tree:

-1.483529864195180e+00 -1.296053859335657e+00
-1.308996938995747e+00 -1.127801743538376e+00
-1.134464013796314e+00 -9.562870818388700e-01
-9.599310885968813e-01 -7.834918877708545e-01
-7.853981633974484e-01 -6.090750919515169e-01
-6.108652381980154e-01 -4.348738075675338e-01
-4.363323129985824e-01 -2.608478200480425e-01
-2.617993877991495e-01 -8.693606119038631e-02
-8.726646259971654e-02 8.726646259971654e-02
8.693606119038631e-02 2.617993877991493e-01
2.608478200480422e-01 4.363323129985823e-01
4.348738075675337e-01 6.108652381980154e-01
6.090750919515169e-01 7.853981633974484e-01
7.834918877708545e-01 9.599310885968813e-01
9.562870818388700e-01 1.134464013796314e+00
1.127801743538376e+00 1.308996938995747e+00
1.296053859335657e+00 1.483529864195180e+00

Search interval:
1.040893537045970e+00 1.570796326794897e+00

Method A: Linear search through interval tree:

std::vector< lib_interval_tree::interval<double> > vecOverlapsA;
lib_interval_tree::interval<double> intSource({lat0, lat1});
for (auto itTargetInterval : inttreeTargetActive) {
    if (itTargetInterval.overlaps(intSource)) {
        vecOverlapsA.push_back(itTargetInterval);
    }
}

Method B: Using overlap_find_all:

std::vector< lib_interval_tree::interval<double> > vecOverlapsB;
inttreeTargetActive.overlap_find_all(
    {lat0, lat1},
    [&it, &vecOverlapsB](lib_interval_tree::interval_tree_t<double>::iterator ittarget){
        vecOverlapsB.push_back(ittarget); return true;},
    false);

Method A produces 3 intervals, while Method B produces only 2 intervals:

A(0) 9.562870818388700e-01 1.134464013796314e+00
A(1) 1.127801743538376e+00 1.308996938995747e+00
A(2) 1.296053859335657e+00 1.483529864195180e+00

B(0) 1.127801743538376e+00 1.308996938995747e+00
B(1) 9.562870818388700e-01 1.134464013796314e+00

Compilation errors in 2.1.0.

I met compilation errors in an example code on README.md.

It works fine in 2.0.0, but it fails in 2.1.0.
Is there any way to fix it?

Environments:
OS: Fedora Linux 40
Compiler: gcc 14.1.1

In file included from test_package.cpp:2:
include/interval-tree/interval_tree.hpp: In instantiation of ‘void lib_interval_tree::increment(T&) [with T = interval_tree_iterator<node<int, interval<int, closed> >, false>]’:
include/interval-tree/interval_tree.hpp:550:26:   required from ‘lib_interval_tree::interval_tree_iterator<node_type, reverse>& lib_interval_tree::interval_tree_iterator<node_type, reverse>::operator++() [with node_type = lib_interval_tree::node<int, lib_interval_tree::interval<int, lib_interval_tree::closed> >; bool reverse = false]’
  550 |                 increment(*this);
      |                 ~~~~~~~~~^~~~~~~
test_package.cpp:23:24:   required from here
   23 |   for (auto const& i : tree)
      |                        ^~~~
include/interval-tree/interval_tree.hpp:625:39: error: ‘lib_interval_tree::interval_tree<lib_interval_tree::interval<int, lib_interval_tree::closed> >::node_type* lib_interval_tree::interval_tree<lib_interval_tree::interval<int, lib_interval_tree::closed> >::root_’ is private within this context
  625 |             iter.node_ = iter.owner_->root_;
      |                          ~~~~~~~~~~~~~^~~~~
include/interval-tree/interval_tree.hpp:1742:20: note: declared private here
 1742 |         node_type* root_;
      |                    ^~~~~
include/interval-tree/interval_tree.hpp:630:31: error: ‘lib_interval_tree::node<int, lib_interval_tree::interval<int, lib_interval_tree::closed> >* lib_interval_tree::node<int, lib_interval_tree::interval<int, lib_interval_tree::closed> >::left_’ is private within this context
  630 |             while(iter.node_->left_)
      |                   ~~~~~~~~~~~~^~~~~
include/interval-tree/interval_tree.hpp:333:15: note: declared private here
  333 |         node* left_;
      |               ^~~~~
include/interval-tree/interval_tree.hpp:631:42: error: ‘lib_interval_tree::node<int, lib_interval_tree::interval<int, lib_interval_tree::closed> >* lib_interval_tree::node<int, lib_interval_tree::interval<int, lib_interval_tree::closed> >::left_’ is private within this context
  631 |                 iter.node_ = iter.node_->left_;
      |                              ~~~~~~~~~~~~^~~~~
include/interval-tree/interval_tree.hpp:333:15: note: declared private here
  333 |         node* left_;
      |               ^~~~~
include/interval-tree/interval_tree.hpp:634:25: error: ‘lib_interval_tree::node<int, lib_interval_tree::interval<int, lib_interval_tree::closed> >* lib_interval_tree::node<int, lib_interval_tree::interval<int, lib_interval_tree::closed> >::right_’ is private within this context
  634 |         if (iter.node_->right_)
      |             ~~~~~~~~~~~~^~~~~~
include/interval-tree/interval_tree.hpp:334:15: note: declared private here
  334 |         node* right_;
      |               ^~~~~~`

test FailBadBorders fails

$ ./tree-tests
...
[==========] 69 tests from 9 test suites ran. (383 ms total)
[  PASSED  ] 68 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] IntervalTests.FailBadBorders

 1 FAILED TEST
$ g++ --version
g++ (GCC) 11.2.1 20210728 (Red Hat 11.2.1-1)

$ arch
x86_64

cmake and cxx requirements are too strict

  1. You bumped the cmake requirement up to 3.21 which is too high and unnecessary -- you can harmlessly lower this, e.g. to 3.14.
  2. You require CXX 14 for generic lambdas such as [](auto const& lhs, auto const& rhs){}. Would be nice to allow CXX 11 also, for maximum compatibility. Try building with cmake -DCMAKE_CXX_STANDARD=11 ..

[Question] Is it safe to erase while executing overlap_find_all?

Hi,

Before all, thanks a lot for sharing this code!

I just wanted to ask if the following is safe to do:

    tree.overlap_find_all(newInterval,
    [&](auto iter) {

        if (newInterval.within(*iter) /* or any other condition */){
            // is it safe to erase while overlap_find_all iterates over the structure?
            newInterval.erase(iter);
        }

        return true;
    }, 
    false);

Best regards,
Manuel.

CompareFunctionT parameter order

Readme says that the compare() functions for find(...) is called with the first argument being the search interval and the second argument being the current search node on the tree. But, the implementation has the order reversed:

if (compare(ptr->interval(), ival))

Readme:

Finds the first interval in the interval tree that has the following statement evaluate to true: compare(ival, interval_in_tree); Allows for propper float comparisons.

Looks like a typo?

Using const iterator for overlap_find

It looks like the const_iterator overlap_find(...) const method calls the non-const overlap_find_i<...>(...) method, which would refuse to compile.

Here is the link to overlap_find:

const_iterator overlap_find(interval_type const& ival, bool exclusive = false) const
{
if (root_ == nullptr)
return end();
if (exclusive)
return const_iterator{overlap_find_i<true>(root_, ival), this};
else
return const_iterator{overlap_find_i<false>(root_, ival), this};
}

Am I missing something or is this incorrectly implemented?

-Suyash

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.