Interval Tree added

This commit is contained in:
IlyaShurupov 2024-02-13 00:18:52 +03:00 committed by Ilya Shurupov
parent 8955a02d05
commit 782a041ba8
6 changed files with 311 additions and 4 deletions

View file

@ -0,0 +1,80 @@
#pragma once
#include "Tree.hpp"
namespace tp {
template <typename tType>
class IntervalKey {
public:
IntervalKey() = default;
IntervalKey(tType start, tType end) {
mStart = start;
mEnd = end;
}
inline bool descentRight(const IntervalKey& in) const { return in.mStart > mStart; }
inline bool descentLeft(const IntervalKey& in) const { return in.mStart <= mStart; }
inline bool exactNode(const IntervalKey& in) const { return in.mStart == mStart && in.mEnd == mEnd; }
inline const IntervalKey& getFindKey() const { return *this; }
inline const IntervalKey& keyInRightSubtree(const IntervalKey& in) const { return in; }
inline const IntervalKey& keyInLeftSubtree(const IntervalKey& in) const { return in; }
template <typename tTreeNodeType>
inline void updateTreeCacheCallBack(const tTreeNodeType& node) {
mMax = 0;
if (node.mRight && node.mRight->key.mMax > mMax) mMax = node.mRight->key.mMax;
if (node.mLeft && node.mLeft->key.mMax > mMax) mMax = node.mLeft->key.mMax;
if (mMax < mEnd) mMax = mEnd;
}
public:
tType mStart = tType();
tType mEnd = tType();
tType mMax = tType();
};
template <typename tType, typename tData>
class IntervalTree : public AvlTree<IntervalKey<tType>, tData> {
typedef AvlTree<IntervalKey<tType>, tData>::Node Node;
public:
IntervalTree() = default;
template <typename tFunctor>
ualni forEachIntersection(tType start, tType end, tFunctor functor) const {
ualni debug = 0;
forEachIntersectionUtil(this->head(), start, end, functor, debug);
return debug;
}
private:
template <typename tFunctor>
void forEachIntersectionUtil(const Node* node, tType start, tType end, tFunctor functor, ualni& debug) const {
if (node == nullptr) return;
debug++;
// If 'start' is to the right of the rightmost point of any interval
// in this node and all children, there won't be any matches.
if (start > node->key.mMax) return;
// Search left children
forEachIntersectionUtil(node->mLeft, start, end, functor, debug);
// Check this node
if (start < node->key.mEnd && end > node->key.mStart) {
functor(node->data);
}
// If end is to the left of the start of this interval,
// then it can't be in any child to the right.
if (end < node->key.mStart) return;
// Otherwise, search right children
forEachIntersectionUtil(node->mRight, start, end, functor, debug);
}
};
}

View file

@ -21,7 +21,8 @@ namespace tp {
inline AvlNumericKey keyInRightSubtree(AvlNumericKey in) const { return in; } inline AvlNumericKey keyInRightSubtree(AvlNumericKey in) const { return in; }
inline AvlNumericKey keyInLeftSubtree(AvlNumericKey in) const { return in; } inline AvlNumericKey keyInLeftSubtree(AvlNumericKey in) const { return in; }
inline void updateTreeCacheCallBack() {} template <typename NodeType>
inline void updateTreeCacheCallBack(const NodeType&) {}
}; };
template <typename Key, typename Data, class Allocator = DefaultAllocator> template <typename Key, typename Data, class Allocator = DefaultAllocator>
@ -42,7 +43,7 @@ namespace tp {
Data data; Data data;
Key key; Key key;
private: public:
Node* mLeft = nullptr; Node* mLeft = nullptr;
Node* mRight = nullptr; Node* mRight = nullptr;
Node* mParent = nullptr; Node* mParent = nullptr;
@ -57,7 +58,7 @@ namespace tp {
inline KeyArg keyInRightSubtree(KeyArg aKey) const { return key.keyInRightSubtree(aKey); } inline KeyArg keyInRightSubtree(KeyArg aKey) const { return key.keyInRightSubtree(aKey); }
inline KeyArg keyInLeftSubtree(KeyArg aKey) const { return key.keyInLeftSubtree(aKey); } inline KeyArg keyInLeftSubtree(KeyArg aKey) const { return key.keyInLeftSubtree(aKey); }
inline void updateTreeCacheCallBack() { key.updateTreeCacheCallBack(); } inline void updateTreeCacheCallBack() { key.updateTreeCacheCallBack(*this); }
}; };
private: private:

View file

@ -0,0 +1,223 @@
#include "IntervalTree.hpp"
#include "Testing.hpp"
#include "Tests.hpp"
#include "Buffer.hpp"
#include <cstdio>
using namespace tp;
struct Interval {
[[nodiscard]] bool overlaps(const Interval& in) const { return in.start < end && in.end > start; }
halnf start{};
halnf end{};
bool ignore = false;
void random(halnf span, halnf scale = 1.f) {
start = ((halnf) randomFloat()) * (span);
end = ((halnf) randomFloat()) * (span);
if (start > end) swap(start, end);
auto len = (end - start) * scale * 0.5f;
auto mid = (start + end) / 2.f;
start = mid - len;
end = mid + len;
}
void randomSized(halnf span, halnf size, halnf wobble) {
start = ((halnf) randomFloat()) * (span);
end = start + size + (halnf) (randomFloat() * wobble);
}
};
TEST_DEF_STATIC(FunctionalitySimple) {
IntervalTree<ualni, ualni> intervalTree;
intervalTree.insert({ 0, 10 }, 1);
intervalTree.insert({ 3, 6 }, 2);
intervalTree.insert({ 8, 12 }, 3);
intervalTree.forEachIntersection(4, 5, [](ualni data) {
printf("%i", int(data));
printf("\n");
});
}
TEST_DEF_STATIC(FunctionalityScale) {
const int NUM_TEST_INTERVALS = 1000;
const halnf SPAN = 100;
Buffer<Interval> pool;
IntervalTree<halnf, ualni> intervalTree;
Buffer<Interval> testIntervals;
auto test = [&]() {
for (auto testInterval : testIntervals) {
Buffer<ualni> correct;
Buffer<ualni> result;
ualni idx = 0;
for (auto interval : pool) {
if (!interval->ignore && interval->overlaps(testInterval.data())) {
correct.append(idx);
}
idx++;
}
intervalTree.forEachIntersection(testInterval->start, testInterval->end, [&](ualni data) {
result.append(data);
});
TEST(correct.size() == result.size());
if (!(correct.size() == result.size())) {
printf("intersections - \n");
for (auto i : correct) {
printf(" %i", (int) i.data());
}
printf("\n");
for (auto i : result) {
printf(" %i", (int) i.data());
}
printf("\n\n");
}
// todo compare containers
}
};
// initialize
for (auto i : Range<ualni>(NUM_TEST_INTERVALS)) {
auto interval = Interval();
interval.random(SPAN);
pool.append(interval);
intervalTree.insert({ interval.start, interval.end }, i);
interval.random(SPAN * 2.f, (halnf) randomFloat());
testIntervals.append(interval);
}
test();
// remove some
for (auto i : Range<ualni>(NUM_TEST_INTERVALS / 2)) {
auto idx = ualni(randomFloat() * (alnf) pool.size());
pool[idx].ignore = true;
intervalTree.remove({ pool[idx].start, pool[idx].end });
}
test();
}
TEST_DEF_STATIC(Efficency) {
struct Stat {
halnf numItems = 0;
halnf avgFound = 0;
halnf avgChecks = 0;
};
auto test = [&](ualni NUM_TEST_INTERVALS, ualni NUM_CHECKS) {
const auto SPAN = (halnf) (halnf(NUM_TEST_INTERVALS)) * 0 + 1000;
const auto SCALE = (halnf) (2.f);
IntervalTree<halnf, ualni> intervalTree;
Buffer<Interval> testIntervals;
auto WOBBLE = SPAN * 0;
for (auto i : Range<ualni>(NUM_TEST_INTERVALS)) {
auto interval = Interval();
interval.randomSized(SPAN, SCALE, WOBBLE);
intervalTree.insert({ interval.start, interval.end }, i);
// WOBBLE -= 1.f;
}
for (auto i : Range(0))
intervalTree.insert({ (halnf) i * 0.01f, SPAN }, 0);
WOBBLE = 0;
for (auto i : Range<ualni>(NUM_CHECKS)) {
auto interval = Interval();
interval.randomSized(SPAN, SCALE, WOBBLE);
testIntervals.append(interval);
}
ualni debugMaxChecks = 0;
ualni debugMaxFound = 0;
halnf debugAvgChecks = 0;
halnf debugAvgFound = 0;
for (auto testInterval : testIntervals) {
ualni debugFound = 0;
ualni debug = intervalTree.forEachIntersection(testInterval->start, testInterval->end, [&](ualni data) {
debugFound++;
//
});
if (debug > debugMaxChecks) {
debugMaxChecks = debug;
debugMaxFound = debugFound;
}
debugMaxChecks = max(debug, debugMaxChecks);
debugAvgChecks += (halnf) debug;
debugAvgFound += (halnf) debugFound;
}
debugAvgChecks /= (halnf) testIntervals.size();
debugAvgFound /= (halnf) testIntervals.size();
printf("\nItems : %llu\n", NUM_TEST_INTERVALS);
printf("Avg(checks) : %f\n", debugAvgChecks);
printf("Avg(found) : %f\n", debugAvgFound);
printf("Max checks : %llu\n", debugMaxChecks);
printf("Max checks found : %llu\n", debugMaxFound);
printf("N(Avg(checks) / Avg(N(items)) : %f\n", debugAvgChecks / (halnf) intervalTree.size());
printf("N(Avg(found)) / Avg(N(items)) : %f\n", debugAvgFound / (halnf) intervalTree.size());
printf("Avg(found) / Avg(checks) : %f\n", debugAvgFound / debugAvgChecks);
return Stat{ (halnf) NUM_TEST_INTERVALS,
debugAvgFound / (halnf) intervalTree.size(),
debugAvgChecks / (halnf) intervalTree.size() };
};
Buffer<Stat> stats;
for (auto i : Range(2, 5)) {
Stat stat = test(pow(10, i), 100);
stats.append(stat);
}
printf("\nChecks: ");
for (auto stat : stats)
printf("%e ", stat->avgChecks);
printf("\nHits: ");
for (auto stat : stats)
printf("%e ", stat->avgFound);
printf("\nItems: ");
for (auto stat : stats)
printf("%e ", stat->numItems);
printf("\n\n");
}
TEST_DEF_STATIC(Benchmarks) {}
TEST_DEF(IntervalTree) {
testFunctionalitySimple();
testFunctionalityScale();
testEfficency();
testBenchmarks();
}

View file

@ -16,6 +16,7 @@ int main() {
return 1; return 1;
} }
testIntervalTree();
testList(); testList();
testMap(); testMap();
testAvl(); testAvl();

View file

@ -34,3 +34,4 @@ void testMap();
void testAvl(); void testAvl();
void testBuffer(); void testBuffer();
void testBuffer2d(); void testBuffer2d();
void testIntervalTree();

View file

@ -93,6 +93,7 @@ namespace tp {
// power // power
template <typename T> template <typename T>
[[nodiscard]] T pow(T x, uhalni n) { [[nodiscard]] T pow(T x, uhalni n) {
if (n == 0) return 1;
T out = x; T out = x;
for (uhalni i = 0; i < n - 1; i++) { for (uhalni i = 0; i < n - 1; i++) {
out = out * x; out = out * x;