From 24ce07df8c16e339fab8e5e14482f3913fda6f9b Mon Sep 17 00:00:00 2001 From: Jonathan Means Date: Mon, 24 Feb 2020 08:47:04 -0500 Subject: [PATCH] Squashed 'externals/coda-oss/' changes from e3e70cf74..df73cbb6e df73cbb6e Merge pull request #345 from mdaus/mem-align 2167292c7 alignment method ebf630751 Merge pull request #344 from mdaus/range-list-type f904fab49 doxygen e59888523 incorrect start element in unit test fd5baf864 Range split 918b3f546 polygon --> types 0a5d6cec2 style 21d8c551b RangeList type git-subtree-dir: externals/coda-oss git-subtree-split: df73cbb6ea5e789546ac5be1f11dba349fa0ed18 --- modules/c++/mem/include/mem/Align.h | 41 +++ modules/c++/mem/source/Align.cpp | 35 +++ modules/c++/mem/source/ScratchMemory.cpp | 19 +- modules/c++/types/include/import/types.h | 1 + modules/c++/types/include/types/Range.h | 14 + modules/c++/types/include/types/RangeList.h | 260 ++++++++++++++++++ modules/c++/types/source/Range.cpp | 49 ++++ modules/c++/types/source/RangeList.cpp | 246 +++++++++++++++++ modules/c++/types/unittests/test_range.cpp | 48 ++++ .../c++/types/unittests/test_range_list.cpp | 222 +++++++++++++++ 10 files changed, 918 insertions(+), 17 deletions(-) create mode 100644 modules/c++/mem/include/mem/Align.h create mode 100644 modules/c++/mem/source/Align.cpp create mode 100644 modules/c++/types/include/types/RangeList.h create mode 100644 modules/c++/types/source/Range.cpp create mode 100644 modules/c++/types/source/RangeList.cpp create mode 100644 modules/c++/types/unittests/test_range_list.cpp diff --git a/modules/c++/mem/include/mem/Align.h b/modules/c++/mem/include/mem/Align.h new file mode 100644 index 000000000..252fe9aa0 --- /dev/null +++ b/modules/c++/mem/include/mem/Align.h @@ -0,0 +1,41 @@ +/* ========================================================================= + * This file is part of mem-c++ + * ========================================================================= + * + * (C) Copyright 2004 - 2020, Radiant Geospatial Solutions + * + * mem-c++ is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; If not, + * see . + * + */ +#ifndef __MEM_ALIGN_H__ +#define __MEM_ALIGN_H__ + +#include + +namespace mem +{ +/*! + * Align the input pointer to lay at the next multiple of + * of the provided alignment. + * + * \param[in, out] data Pointer to the address to align to a multiple + * alignment + * \param alignment The alignment to use + */ +void align(sys::ubyte* __restrict* data, + size_t alignment = sys::SSE_INSTRUCTION_ALIGNMENT); +} + +#endif diff --git a/modules/c++/mem/source/Align.cpp b/modules/c++/mem/source/Align.cpp new file mode 100644 index 000000000..d007004a9 --- /dev/null +++ b/modules/c++/mem/source/Align.cpp @@ -0,0 +1,35 @@ +/* ========================================================================= + * This file is part of mem-c++ + * ========================================================================= + * + * (C) Copyright 2004 - 2020, Radiant Geospatial Solutions + * + * mem-c++ is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; If not, + * see . + * + */ +#include + +namespace mem +{ +void align(sys::ubyte* __restrict* data, size_t alignment) +{ + const size_t beyondBoundary = reinterpret_cast(*data) % + alignment; + if (beyondBoundary != 0) + { + *data += alignment - beyondBoundary; + } +} +} diff --git a/modules/c++/mem/source/ScratchMemory.cpp b/modules/c++/mem/source/ScratchMemory.cpp index d1e4632ac..bcedacaf0 100644 --- a/modules/c++/mem/source/ScratchMemory.cpp +++ b/modules/c++/mem/source/ScratchMemory.cpp @@ -19,25 +19,10 @@ * see . * */ +#include #include -namespace -{ -/*! - * \brief Advance sys::ubyte pointer to next alignment boundary - */ -void align(sys::ubyte*& dataPtr, size_t alignment) -{ - const size_t beyondBoundary = reinterpret_cast(dataPtr) % alignment; - - if (beyondBoundary != 0) - { - dataPtr += alignment - beyondBoundary; - } -} -} - namespace mem { ScratchMemory::ScratchMemory() : @@ -218,7 +203,7 @@ void ScratchMemory::setup(const BufferView& scratchBuffer) for (size_t i = 0; i < segment.numBuffers; ++i) { segment.buffers[i] = mBuffer.data + currentOffset; - align(segment.buffers[i], segment.alignment); + align(&segment.buffers[i], segment.alignment); currentOffset = segment.buffers[i] + segment.numBytes - mBuffer.data; } diff --git a/modules/c++/types/include/import/types.h b/modules/c++/types/include/import/types.h index f32631e0e..ac5516076 100644 --- a/modules/c++/types/include/import/types.h +++ b/modules/c++/types/include/import/types.h @@ -27,5 +27,6 @@ #include #include #include +#include #endif diff --git a/modules/c++/types/include/types/Range.h b/modules/c++/types/include/types/Range.h index 2ba983ec8..f4bdae241 100644 --- a/modules/c++/types/include/types/Range.h +++ b/modules/c++/types/include/types/Range.h @@ -159,6 +159,20 @@ struct Range return overlap.mNumElements; } + /*! + * Given an input range, removes numElementsReq elements from the end of it + * (if possible), and provides the new range composed of these elements + * + * \param inputRange Initial Range object + * \param numElementsReq The number elements requested to be split from + * the end of inputRange + * + * \returns The resulting range formed from the removed elements of + * inputRange. Has no more than numElementsReq elements, + * but may have fewer depending on the size of inputRange. + */ + Range split(size_t numElementsReq) const; + //! \return True if there are no elements in this range, false otherwise bool empty() const { diff --git a/modules/c++/types/include/types/RangeList.h b/modules/c++/types/include/types/RangeList.h new file mode 100644 index 000000000..7d3b91388 --- /dev/null +++ b/modules/c++/types/include/types/RangeList.h @@ -0,0 +1,260 @@ +/* ========================================================================= + * This file is part of types-c++ + * ========================================================================= + * + * (C) Copyright 2004 - 2020, Radiant Geospatial Solutions + * + * types-c++ is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; If not, + * see . + * + */ +#ifndef __TYPES_RANGE_LIST_H__ +#define __TYPES_RANGE_LIST_H__ + +#include +#include + +#include + +namespace types +{ +/*! + * \class RangeList + * \brief Maintain an ordered collection of disjoint types::Range objects + * + * RangeList is meant to hold, and provide operations on, a set of ordered, + * disjoint types::Range objects. Range objects are ordered by increasing + * mStartElement. + * + * +-------------+ +----------+ +-------------+ + * | range 0 | | range 1 | | range 2 | + * +-------------+ +----------+ +-------------+ + * |--------------------------------------------------------------> + * 0 max(size_t) + * + * Many of the operations are O(N), where N is the number of ranges in the + * collection. As a result, caution should be taken when using this class + * that N does not grow large enough to begin having a noticeable impact. + * + * TODO: Take a look at replacing the internals with some interval list + * structure. + */ +class RangeList +{ +public: + /*! + * Default constructor initializing an empty list + */ + RangeList() = default; + + /*! + * Constructor initializing the range list from a single range + * + * \param range Initial range + */ + RangeList(const types::Range& range); + + /*! + * Constructor initializing the range list from a vector of ranges. + * + * Initial ranges do not need to be disjoint, as insert() will be + * called for each element in 'ranges', and consequently performing any + * merges that need to occur. + * + * \param ranges Initial ranges + */ + RangeList(const std::vector& ranges); + + /*! + * Insert a range of size 1 at the given point + * + * O(N) operation, where N is the number of ranges already in the list. + * If point overlaps/touches existing ranges, they will be merged. + * + * \param point The point to insert + */ + void insert(size_t point) + { + insert(types::Range(point, 1)); + } + + /*! + * Insert a range + * + * O(N) operation, where N is the number of ranges already in the list. + * If range overlaps/touches existing ranges, they will be merged. + * In set notation, the resulting list will be union(L, range), where + * L is the the original list of ranges. + * + * \verbatim + * + * original +-----+ +----------+ +-------------+ + * list | | | | | | + * +-----+ +----------+ +-------------+ + * range to +---------------------------+ + * insert | | + * +---------------------------+ + * final +-----+ +-------------------------------------+ + * list | | | | + * +-----+ +-------------------------------------+ + * + * \endverbatim + * + * \param range The range to insert + */ + void insert(const types::Range& range); + + /*! + * Insert a vector of ranges. + * + * O(M*N) operation, M is the size of 'ranges', and + * N is the number of ranges already in the list. + * + * Will merge overlapping/touching ranges. + * + * \param ranges Vector of ranges to insert + */ + void insert(const std::vector& ranges); + + /*! + * Remove a range of size 1 at the given point + * + * O(N) operation, where N is the number of ranges in the list + * If point lies inside a range, the range will be split in two. + * + * \param point The point to remove + */ + void remove(size_t point) + { + remove(types::Range(point, 1)); + } + + /*! + * Remove a range from the list + * + * O(N) operation, where N is the number of ranges already in the list. + * In set notation, the resulting list will be L - intersection(L, range), + * where L is the original list of ranges. + * + * \verbatim + * + * original +-------------+ +----------+ +-------------+ + * list | | | | | | + * +-------------+ +----------+ +-------------+ + * range to +---------------------------+ + * remove | | + * +---------------------------+ + * final +---------+ +----------+ + * list | | | | + * +---------+ +----------+ + * + * \endverbatim + * + * \param range The range to remove + */ + void remove(const types::Range& range); + + /*! + * Remove a vector of ranges + * + * O(M*N) operation, M is the size of 'ranges', and + * N is the number of ranges in the list. + * + * \param ranges Vector of ranges to remove + */ + void remove(const std::vector& ranges); + + /*! + * \returns the number ranges in the list + */ + size_t getNumRanges() const + { + return mRangeList.size(); + } + + /*! + * \returns the total number elements as the sum(R.mNumElements) for + * all ranges R in the list + */ + size_t getTotalNumElements() const; + + /*! + * \returns true if getTotalNumElements() == 0 + */ + bool empty() const + { + return getTotalNumElements() == 0; + } + + /*! + * Get the vector of ranges that compose the range list + * + * Note that remove()/insert()/expand() will invalidate the + * returned reference. + * + * \return the vector of ranges composing the range list + */ + const std::vector& getRanges() const + { + return mRangeList; + } + + /*! + * Expand all ranges in the list by the given expansion factor, applied + * to both the start and end of the range. + * If the resulting expanded ranges touch/overlap, merging will occur. + * + * \param expansion Amount of expansion to apply to both the start + * and end of each range in the list + * \param maxEndElement Maximum end element for the range list. + * There is an implicit minimum starting element of + * 0 (due to size_t), but this allows us to + * specify a maximum end element, particularly useful + * in an image setting, where you may not want to + * go outside of a row or column dimension. + * Default std::numeric_limits::max(). + */ + void expand(size_t expansion, + size_t maxEndElement = std::numeric_limits::max()); + + /*! + * Determine a new range list that is the intersection of the current + * list and another RangeList. + * + * \verbatim + * + * our +--------+ +-------+ +--------+ +-+ +-+ +-+ + * list | | | | | | | | | | | | + * +--------+ +-------+ +--------+ +-+ +-+ +-+ + * other +----------+ +---+ +-+ +-+ +-------+ +-+ + * list | | | | | | | | | | | | + * +----------+ +---+ +-+ +-+ +-------+ +-+ + * intersect +--------+ +---+ +-+ +-+ +-+ +-+ + * | | | | | | | | | | | | + * +--------+ +---+ +-+ +-+ +-+ +-+ + * + * \endverbatim + * + * \param other RangeList to intersect with + * \returns new RangeList that is the intersection of this and other + */ + RangeList intersect(const RangeList& other) const; + +private: + using List = std::vector; + List mRangeList; +}; +} + +#endif diff --git a/modules/c++/types/source/Range.cpp b/modules/c++/types/source/Range.cpp new file mode 100644 index 000000000..4478adf69 --- /dev/null +++ b/modules/c++/types/source/Range.cpp @@ -0,0 +1,49 @@ +/* ========================================================================= + * This file is part of types-c++ + * ========================================================================= + * + * (C) Copyright 2004 - 2020, Radiant Geospatial Solutions + * + * types-c++ is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; If not, + * see . + * + */ +#include + +namespace types +{ +Range Range::split(size_t numElementsReq) const +{ + // If the input range is empty, we can't remove any elements from it... + if (empty() || numElementsReq == 0) + { + return types::Range(); + } + + // If fewer elements are requested than are in inputRange, + // pull off numElementsReq elements and update the split range + if (numElementsReq <= mNumElements) + { + return types::Range(endElement() - numElementsReq, + numElementsReq); + } + // Otherwise, if more elements are requested than are in inputRange, + // set the split range to be input range (denoting that we have taken + // all of the elements) + else + { + return types::Range(mStartElement, mNumElements); + } +} +} diff --git a/modules/c++/types/source/RangeList.cpp b/modules/c++/types/source/RangeList.cpp new file mode 100644 index 000000000..5b258cf2b --- /dev/null +++ b/modules/c++/types/source/RangeList.cpp @@ -0,0 +1,246 @@ +/* ========================================================================= + * This file is part of types-c++ + * ========================================================================= + * + * (C) Copyright 2004 - 2020, Radiant Geospatial Solutions + * + * types-c++ is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; If not, + * see . + * + */ +#include + +#include + +namespace +{ +std::vector +difference(const types::Range& orig, const types::Range& overlap) +{ + // If the original range is fully contained by the overlap, + // the difference is the empty set + if (overlap.containsAll(orig.mStartElement, orig.mNumElements)) + { + return {}; + } + + std::vector result; + + // Create left range + if (orig.mStartElement < overlap.mStartElement) + { + result.emplace_back( + orig.mStartElement, + overlap.mStartElement - orig.mStartElement); + } + + // Create right range + if (orig.endElement() > overlap.endElement()) + { + result.emplace_back( + overlap.endElement(), + orig.endElement() - overlap.endElement()); + } + return result; +} +} + +namespace types +{ +RangeList::RangeList(const types::Range& range) : + mRangeList(0) +{ + insert(range); +} + +RangeList::RangeList(const std::vector& ranges) : + mRangeList(0) +{ + for (const auto& inputRange : ranges) + { + insert(inputRange); + } +} + +size_t RangeList::getTotalNumElements() const +{ + size_t count = 0; + for (const auto& range : mRangeList) + { + count += range.mNumElements; + } + return count; +} + +void RangeList::insert(const std::vector& ranges) +{ + for (const auto& range : ranges) + { + insert(range); + } +} + +void RangeList::insert(const types::Range& range) +{ + if (range.empty()) + { + return; + } + + if (mRangeList.empty()) + { + mRangeList.push_back(range); + return; + } + + std::vector newList; + newList.reserve(mRangeList.size() + 1); + + // TODO: This can be sped up with a log(N) binary search and an insert + // into the newList from mRangeList + size_t ii = 0; + for (; ii < mRangeList.size(); ++ii) + { + if (mRangeList[ii].endElement() < range.mStartElement) + { + newList.push_back(mRangeList[ii]); + } + else + { + break; + } + } + + newList.push_back(range); + + for (; ii < mRangeList.size(); ++ii) + { + const types::Range& oldRange = mRangeList[ii]; + types::Range& backRange = newList.back(); + + if (backRange.overlaps(oldRange) || backRange.touches(oldRange)) + { + const size_t start = std::min(backRange.mStartElement, + oldRange.mStartElement); + + const size_t end = std::max(backRange.endElement(), + oldRange.endElement()); + + backRange.mStartElement = start; + backRange.mNumElements = end - start; + } + else + { + newList.push_back(oldRange); + } + } + + mRangeList.swap(newList); +} + +void RangeList::remove(const std::vector& ranges) +{ + for (const auto& range : ranges) + { + remove(range); + } +} + +void RangeList::remove(const types::Range& range) +{ + if (range.empty()) + { + return; + } + + std::vector newList; + newList.reserve(mRangeList.size() + 1); + + for (const auto& oldRange : mRangeList) + { + types::Range overlap; + const bool isOverlapping = oldRange.overlaps(range, overlap); + + if (isOverlapping) + { + for (const auto& diff : difference(oldRange, overlap)) + { + newList.push_back(diff); + } + } + else + { + newList.push_back(oldRange); + } + } + + mRangeList.swap(newList); +} + +void RangeList::expand(size_t expansion, size_t maxEndElement) +{ + if (expansion == 0) + { + return; + } + + const List oldRanges = getRanges(); + mRangeList.clear(); + + for (const auto& range : oldRanges) + { + const size_t start = + (range.mStartElement >= expansion) ? + range.mStartElement - expansion : 0; + + const size_t end = + std::min(range.endElement() + expansion, maxEndElement); + + insert(types::Range(start, end - start)); + } +} + +RangeList RangeList::intersect(const RangeList& other) const +{ + RangeList output; + auto iterA = std::begin(getRanges()); + auto iterB = std::begin(other.getRanges()); + const auto endIterA = std::end(getRanges()); + const auto endIterB = std::end(other.getRanges()); + + while ((iterA != endIterA) && (iterB != endIterB)) + { + const size_t start = + std::max(iterA->mStartElement, iterB->mStartElement); + const size_t end = + std::min(iterA->endElement(), iterB->endElement()); + + if (start < end) + { + output.insert(types::Range(start, end - start)); + } + + if (iterA->endElement() < iterB->endElement()) + { + ++iterA; + } + else + { + ++iterB; + } + } + + return output; +} +} diff --git a/modules/c++/types/unittests/test_range.cpp b/modules/c++/types/unittests/test_range.cpp index ef9c24c9b..7f62f7bcc 100644 --- a/modules/c++/types/unittests/test_range.cpp +++ b/modules/c++/types/unittests/test_range.cpp @@ -19,8 +19,10 @@ * see . * */ +#include #include "TestCase.h" + #include namespace @@ -96,11 +98,57 @@ TEST_CASE(TestTouches) TEST_ASSERT_FALSE(B.touches(A)); } } + +TEST_CASE(TestSplit) +{ + // Test splitting all elements + { + const types::Range A(5, 10); + const types::Range splitRange = A.split(10); + TEST_ASSERT_EQ(splitRange.mNumElements, 10); + TEST_ASSERT_EQ(splitRange.mStartElement, 5); + } + + // Test splitting a portion of elements + { + const types::Range A(5, 10); + const types::Range splitRange = A.split(5); + TEST_ASSERT_EQ(splitRange.mNumElements, 5); + TEST_ASSERT_EQ(splitRange.mStartElement, 10); + } + + // Test splitting zero elements + { + const types::Range A(5, 10); + const types::Range splitRange = A.split(0); + TEST_ASSERT_EQ(splitRange.mNumElements, 0); + TEST_ASSERT_EQ(splitRange.mStartElement, + std::numeric_limits::max()); + } + + // Test splitting more elements than are available + { + const types::Range A(5, 10); + const types::Range splitRange = A.split(20); + TEST_ASSERT_EQ(splitRange.mNumElements, 10); + TEST_ASSERT_EQ(splitRange.mStartElement, 5); + } + + // Test splitting from an empty range + { + const types::Range A(0, 0); + const types::Range splitRange = A.split(10); + TEST_ASSERT_EQ(splitRange.mNumElements, 0); + TEST_ASSERT_EQ(splitRange.mStartElement, + std::numeric_limits::max()); + } +} } int main(int /*argc*/, char** /*argv*/) { TEST_CHECK(TestGetNumSharedElements); TEST_CHECK(TestTouches); + TEST_CHECK(TestSplit); return 0; } diff --git a/modules/c++/types/unittests/test_range_list.cpp b/modules/c++/types/unittests/test_range_list.cpp new file mode 100644 index 000000000..d1c72cd7f --- /dev/null +++ b/modules/c++/types/unittests/test_range_list.cpp @@ -0,0 +1,222 @@ +/* ========================================================================= + * This file is part of types-c++ + * ========================================================================= + * + * (C) Copyright 2004 - 2020, Radiant Geospatial Solutions + * + * types-c++ is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; If not, + * see . + * + */ +#include + +#include "TestCase.h" +#include + +namespace +{ +TEST_CASE(TestDisjointInsertion) +{ + types::RangeList RL; + RL.insert({3, 4}); // [3, 7) + RL.insert({9, 2}); // [9, 11) + RL.insert({1, 1}); // [1, 2) + + const std::vector ranges = RL.getRanges(); + + TEST_ASSERT_EQ(ranges.size(), 3); + TEST_ASSERT_EQ(ranges[0].mStartElement, 1); + TEST_ASSERT_EQ(ranges[0].mNumElements, 1); + TEST_ASSERT_EQ(ranges[1].mStartElement, 3); + TEST_ASSERT_EQ(ranges[1].mNumElements, 4); + TEST_ASSERT_EQ(ranges[2].mStartElement, 9); + TEST_ASSERT_EQ(ranges[2].mNumElements, 2); +} + +TEST_CASE(TestMergedInsertion) +{ + types::RangeList RL; + RL.insert({3, 4}); // [3, 7) + RL.insert({9, 2}); // [9, 11) + RL.insert({1, 1}); // [1, 2) + RL.insert({2, 1}); // [2, 3) merging starts now + RL.insert({6, 6}); // [6, 12) + // Final range should be [1, 12) + + const std::vector ranges = RL.getRanges(); + + TEST_ASSERT_EQ(ranges.size(), 1); + TEST_ASSERT_EQ(ranges[0].mStartElement, 1); + TEST_ASSERT_EQ(ranges[0].mNumElements, 11); +} + +TEST_CASE(TestSinglePointTouching) +{ + types::RangeList RL; + RL.insert(2); + RL.insert(4); + RL.insert(6); + RL.insert(7); + RL.insert(5); + RL.insert(1); + RL.insert(3); + // Final range should be [1, 8) + + const std::vector ranges = RL.getRanges(); + + TEST_ASSERT_EQ(ranges.size(), 1); + TEST_ASSERT_EQ(ranges[0].mStartElement, 1); + TEST_ASSERT_EQ(ranges[0].mNumElements, 7); +} + +TEST_CASE(TestRemoveFromMiddle) +{ + types::RangeList RL(types::Range(10, 10)); // [10, 20) + RL.remove({11, 5}); // [11, 16) + // Final ranges should be [10, 11), [16, 20) + + const std::vector ranges = RL.getRanges(); + + TEST_ASSERT_EQ(ranges.size(), 2); + TEST_ASSERT_EQ(ranges[0].mStartElement, 10); + TEST_ASSERT_EQ(ranges[0].mNumElements, 1); + TEST_ASSERT_EQ(ranges[1].mStartElement, 16); + TEST_ASSERT_EQ(ranges[1].mNumElements, 4); +} + +TEST_CASE(TestRemoveFromLeft) +{ + types::RangeList RL(types::Range(10, 10)); // [10, 20) + RL.remove({8, 5}); // [8, 13) + // Final range should be [13, 20) + + const std::vector ranges = RL.getRanges(); + + TEST_ASSERT_EQ(ranges.size(), 1); + TEST_ASSERT_EQ(ranges[0].mStartElement, 13); + TEST_ASSERT_EQ(ranges[0].mNumElements, 7); +} + +TEST_CASE(TestRemoveFromRight) +{ + types::RangeList RL(types::Range(10, 10)); // [10, 20) + RL.remove({15, 10}); // [15, 25) + // Final range should be [10, 15) + + const std::vector ranges = RL.getRanges(); + + TEST_ASSERT_EQ(ranges.size(), 1); + TEST_ASSERT_EQ(ranges[0].mStartElement, 10); + TEST_ASSERT_EQ(ranges[0].mNumElements, 5); +} + +TEST_CASE(TestRemoveMultiRangeOverlap) +{ + const std::vector initRanges = + { + { 0, 5}, // [0, 5) + {10, 10}, // [10, 20) + {25, 5} // [25, 30) + }; + + types::RangeList RL(initRanges); + RL.remove({2, 25}); // [2, 27) + // Final ranges should be [0, 2), [27, 30) + + const std::vector ranges = RL.getRanges(); + + TEST_ASSERT_EQ(ranges.size(), 2); + TEST_ASSERT_EQ(ranges[0].mStartElement, 0); + TEST_ASSERT_EQ(ranges[0].mNumElements, 2); + TEST_ASSERT_EQ(ranges[1].mStartElement, 27); + TEST_ASSERT_EQ(ranges[1].mNumElements, 3); +} + +TEST_CASE(TestExpansion) +{ + const std::vector initRanges = + { + { 1, 3}, // [1, 4) + {10, 10}, // [10, 20) + {24, 5} // [24, 29) + }; + + types::RangeList RL(initRanges); + RL.expand(2); + // Final ranges should be [0, 6), [8, 31) + + const std::vector ranges = RL.getRanges(); + + TEST_ASSERT_EQ(ranges.size(), 2); + TEST_ASSERT_EQ(ranges[0].mStartElement, 0); + TEST_ASSERT_EQ(ranges[0].mNumElements, 6); + TEST_ASSERT_EQ(ranges[1].mStartElement, 8); + TEST_ASSERT_EQ(ranges[1].mNumElements, 23); +} + +TEST_CASE(TestIntersection) +{ + // Compute the intersection C = A.intersect(B) + // A: |--------| |-------| |--------| |-| |-| |-| + // B: |----------| |---| |-| |-| |-------| |-| + // C: |--------| |---| |-| |-| |-| |-| + + const types::RangeList A(std::vector( + { + {2, 5}, + {10, 5}, + {20, 10}, + {31, 2}, + {37, 1}, + {45, 1} + + })); + + const types::RangeList B(std::vector( + { + {1, 6}, + {12, 2}, + {22, 1}, + {26, 3}, + {30, 10}, + {46, 1} + })); + + // Final ranges: [2, 7), [12, 14), [22, 23), [26, 29), [31, 33), [37, 38) + const types::RangeList C = A.intersect(B); + + const std::vector ranges = C.getRanges(); + TEST_ASSERT_EQ(ranges.size(), 6); + TEST_ASSERT_TRUE(types::Range(2,5) == ranges[0]); + TEST_ASSERT_TRUE(types::Range(12,2) == ranges[1]); + TEST_ASSERT_TRUE(types::Range(22,1) == ranges[2]); + TEST_ASSERT_TRUE(types::Range(26,3) == ranges[3]); + TEST_ASSERT_TRUE(types::Range(31,2) == ranges[4]); + TEST_ASSERT_TRUE(types::Range(37,1) == ranges[5]); +} +} + +int main(int /*argc*/, char** /*argv*/) +{ + TEST_CHECK(TestDisjointInsertion); + TEST_CHECK(TestMergedInsertion); + TEST_CHECK(TestSinglePointTouching); + TEST_CHECK(TestRemoveFromMiddle); + TEST_CHECK(TestRemoveFromLeft); + TEST_CHECK(TestRemoveFromRight); + TEST_CHECK(TestRemoveMultiRangeOverlap); + TEST_CHECK(TestExpansion); + TEST_CHECK(TestIntersection); + return 0; +}