| // Copyright 2017-2020 The Verible Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #ifndef VERIBLE_COMMON_UTIL_INTERVAL_SET_H_ | |
| #define VERIBLE_COMMON_UTIL_INTERVAL_SET_H_ | |
| #include <initializer_list> | |
| #include <iostream> | |
| #include <map> | |
| #include <sstream> | |
| #include <utility> | |
| #include "absl/random/random.h" | |
| #include "absl/strings/numbers.h" | |
| #include "absl/strings/str_join.h" | |
| #include "absl/strings/str_split.h" | |
| #include "common/util/auto_iterator.h" | |
| #include "common/util/interval.h" | |
| #include "common/util/iterator_range.h" | |
| #include "common/util/logging.h" | |
| namespace verible { | |
| // Prints a sequence of intervals to stream. | |
| // Iter can point to any Interval<> or any type constructible to Interval<> | |
| // (like std::pair). | |
| template <class Iter> | |
| std::ostream& FormatIntervals(std::ostream& stream, Iter begin, Iter end) { | |
| typedef typename std::iterator_traits<Iter>::value_type value_type; | |
| return stream << absl::StrJoin( | |
| begin, end, ", ", | |
| [](std::string* out, const value_type& interval) { | |
| std::ostringstream temp_stream; | |
| temp_stream << AsInterval(interval); | |
| out->append(temp_stream.str()); | |
| }); | |
| } | |
| // Non-template private implementation class of IntervalSet. | |
| class _IntervalSetImpl { | |
| protected: | |
| // Returns the first (interval) iterator that spans or follows 'value'. | |
| // Implementing this way avoids duplication between the const_iterator and | |
| // iterator variants. | |
| template <typename M> // M is a map-type | |
| static typename auto_iterator_selector<M>::type // const_iterator or iterator | |
| FindLowerBound(M& intervals, const typename M::mapped_type& value) { | |
| const auto iter = intervals.lower_bound(value); | |
| if (iter == intervals.begin()) { | |
| return iter; | |
| } | |
| // Check whether the previous interval's .max spans 'value'. | |
| const auto prev = std::prev(iter); | |
| if (AsInterval(*prev).contains(value)) { | |
| return prev; | |
| } | |
| return iter; | |
| } | |
| template <typename M> // M is a map-type | |
| static typename auto_iterator_selector<M>::type // const_iterator or iterator | |
| FindSpanningInterval(M& intervals, | |
| const Interval<typename M::mapped_type>& interval) { | |
| CHECK(interval.valid()); | |
| // Nothing 'contains' an empty interval. | |
| if (interval.empty()) return intervals.end(); | |
| auto iter = intervals.upper_bound(interval.min); | |
| // lower_bound misses equality condition | |
| if (iter != intervals.begin()) { | |
| const auto prev = std::prev(iter); // look at interval before | |
| CHECK_LE(prev->first, interval.min); | |
| if (AsInterval(*prev).contains(interval)) { | |
| return prev; | |
| } | |
| } | |
| // else interval.min is already less than lowest interval | |
| return intervals.end(); | |
| } | |
| // Variant that finds an interval that covers a single value. | |
| // This is more efficient than checking for [value, value +1). | |
| template <typename M> // M is a map-type | |
| static typename auto_iterator_selector<M>::type // const_iterator or iterator | |
| FindSpanningInterval(M& intervals, const typename M::mapped_type& value) { | |
| const auto iter = intervals.upper_bound(value); | |
| // lower_bound misses equality condition | |
| if (iter != intervals.begin()) { | |
| const auto prev = std::prev(iter); // look at interval before | |
| if (AsInterval(*prev).contains(value)) { | |
| return prev; | |
| } | |
| // else value is greater than upper bound of this interval | |
| } | |
| // else value is less than lower bound of first interval | |
| return intervals.end(); | |
| } | |
| }; | |
| // IntervalSet represents a set of integral values. | |
| // Set membership is efficiently represented as a collection of | |
| // non-overlapping [min, max) intervals. | |
| // Mutating operations will automatically merge abutting intervals. | |
| template <typename T> | |
| class IntervalSet : private _IntervalSetImpl { | |
| private: | |
| typedef std::map<T, T> impl_type; | |
| protected: | |
| typedef typename impl_type::iterator iterator; | |
| typedef typename impl_type::reverse_iterator reverse_iterator; | |
| public: | |
| typedef typename impl_type::value_type value_type; | |
| typedef typename impl_type::const_iterator const_iterator; | |
| typedef typename impl_type::const_reverse_iterator const_reverse_iterator; | |
| typedef typename impl_type::size_type size_type; | |
| public: | |
| IntervalSet() = default; | |
| IntervalSet(std::initializer_list<Interval<T>> ranges) { | |
| // Add-ing will properly fuse overlapping intervals and maintain intervals_' | |
| // invariants. | |
| for (const auto& range : ranges) { | |
| Add(range); | |
| } | |
| } | |
| IntervalSet(const IntervalSet<T>&) = default; | |
| IntervalSet(IntervalSet<T>&&) = default; | |
| ~IntervalSet() { CheckIntegrity(); } | |
| IntervalSet<T>& operator=(const IntervalSet<T>&) = default; | |
| IntervalSet<T>& operator=(IntervalSet<T>&&) = default; | |
| public: | |
| const_iterator begin(void) const { return intervals_.begin(); } | |
| const_iterator end(void) const { return intervals_.end(); } | |
| // Returns the number of disjoint intervals that compose this set. | |
| size_type size(void) const { return intervals_.size(); } | |
| // Returns sum of sizes of all intervals. | |
| size_type sum_of_sizes(void) const; | |
| // Returns true if the set contains no intervals/values. | |
| bool empty(void) const { return intervals_.empty(); } | |
| // Remove all intervals from the set. | |
| void clear(void) { intervals_.clear(); } | |
| void swap(IntervalSet<T>& other) { intervals_.swap(other.intervals_); } | |
| bool operator==(const IntervalSet<T>& other) const { | |
| return intervals_ == other.intervals_; | |
| } | |
| bool operator!=(const IntervalSet<T>& other) const { | |
| return !(*this == other); | |
| } | |
| // Returns true if value is a member of an interval in the set. | |
| bool Contains(const T& value) const { | |
| return Find(value) != intervals_.end(); | |
| } | |
| // Returns true if interval is entirely contained by an interval in the set. | |
| // If interval is empty, return false. | |
| bool Contains(const Interval<T>& interval) const { | |
| return Find(interval) != intervals_.end(); | |
| } | |
| // TODO(fangism): bool Contains(const IntervalSet<T>& interval) const; | |
| // Returns the first (interval) iterator that spans or follows 'value'. | |
| const_iterator LowerBound(const T& value) const { | |
| return _IntervalSetImpl::FindLowerBound(intervals_, value); | |
| } | |
| // Returns the first (interval) iterator that follows 'value'. | |
| const_iterator UpperBound(const T& value) const { | |
| return intervals_.upper_bound(value); | |
| } | |
| // Returns an iterator to the interval that entirely contains [min,max), | |
| // or the end iterator if no such interval exists, or the input is empty. | |
| const_iterator Find(const Interval<T>& interval) const { | |
| return _IntervalSetImpl::FindSpanningInterval(intervals_, interval); | |
| } | |
| // Returns an iterator to the interval that contains 'value', | |
| // or the end iterator if no such interval exists. | |
| const_iterator Find(const T& value) const { | |
| return _IntervalSetImpl::FindSpanningInterval(intervals_, value); | |
| } | |
| // Adds an interval to the interval set. | |
| // Also fuses any intervals that may result from the addition. | |
| void Add(const Interval<T>& interval) { | |
| CHECK(interval.valid()); | |
| if (interval.empty()) return; // adding empty interval changes nothing | |
| const auto& min = interval.min; | |
| const auto& max = interval.max; | |
| T new_max = max; | |
| iterator erase_end; | |
| const auto max_lb = LowerBound(max); | |
| if (max_lb == intervals_.end()) { | |
| // erase all the way to the end | |
| erase_end = max_lb; | |
| } else if (AsInterval(*max_lb).contains(max)) { | |
| // erase this interval, but use its max (.second) | |
| erase_end = std::next(max_lb); | |
| new_max = max_lb->second; | |
| } else { | |
| // erase up to the interval before this one | |
| erase_end = max_lb; | |
| } | |
| iterator erase_begin; | |
| const auto p = intervals_.insert({min, new_max}); // (bool, iterator) | |
| if (p.second) { | |
| // new interval was successfully inserted at p.first | |
| // If this abuts or overlaps with the previous interval, update that | |
| // one with new_max, and discard this one. | |
| if (p.first == intervals_.begin()) { | |
| erase_begin = std::next(p.first); | |
| } else { | |
| const auto prev = std::prev(p.first); | |
| if (prev->second >= min) { | |
| prev->second = new_max; | |
| erase_begin = p.first; | |
| } else { | |
| erase_begin = std::next(p.first); | |
| } | |
| } | |
| } else { | |
| // new interval was not inserted because there already exist key=min. | |
| // Re-use that interval, but update its max. | |
| p.first->second = new_max; | |
| // Erase intervals starting after that one. | |
| erase_begin = std::next(p.first); | |
| } | |
| // Finally erase range of obsolete intervals to maintain invariants. | |
| intervals_.erase(erase_begin, erase_end); | |
| CheckIntegrity(); | |
| } | |
| // Adds a single value to the interval set. | |
| void Add(const T& value) { Add({value, value + 1}); } | |
| // Removes an interval from the set. | |
| // Run-time: O(lg N), where N is the number of existing intervals. | |
| void Difference(const Interval<T>& interval) { | |
| CHECK(interval.valid()); | |
| if (interval.empty()) return; // removing an empty interval changes nothing | |
| const auto& min = interval.min; | |
| const auto& max = interval.max; | |
| iterator erase_end; | |
| bool replace_upper = false; | |
| Interval<T> replaced_upper_interval; | |
| const auto max_ub = UpperBound(max); | |
| if (max_ub == intervals_.begin()) { | |
| return; // interval is out of range of this set | |
| } | |
| { | |
| const auto prev = std::prev(max_ub); | |
| if (AsInterval(*prev).contains(max)) { | |
| if (prev->first == max) { | |
| erase_end = prev; // erase up to this interval | |
| } else { | |
| // max falls in the middle of this prev interval | |
| erase_end = max_ub; // erase including this interval | |
| // Add new interval with higher min, which will be ordered after prev. | |
| replaced_upper_interval = {max, prev->second}; | |
| replace_upper = replaced_upper_interval.valid() && | |
| !replaced_upper_interval.empty(); | |
| } | |
| } else { | |
| erase_end = max_ub; | |
| } | |
| } | |
| iterator erase_begin; | |
| bool replace_lower = false; | |
| Interval<T> replaced_lower_interval; | |
| const auto min_lb = LowerBound(min); | |
| if (min_lb == intervals_.end()) { | |
| return; // interval is out of range of this set | |
| } else if (AsInterval(*min_lb).contains(min)) { | |
| if (min_lb->first == min) { | |
| erase_begin = min_lb; // erase starting at this interval | |
| replaced_lower_interval = {max, min_lb->second}; | |
| replace_lower = | |
| replaced_lower_interval.valid() && !replaced_lower_interval.empty(); | |
| } else { | |
| // FIXME | |
| min_lb->second = min; // reduce the upper bound of matching interval | |
| erase_begin = std::next(min_lb); // erase starting past this interval | |
| } | |
| } else { // min is to the left of the first interval we might erase | |
| erase_begin = min_lb; | |
| } | |
| // Erase range of obsolete intervals. | |
| intervals_.erase(erase_begin, erase_end); | |
| // Add new interval as necessary. | |
| if (replace_lower) { | |
| AddUnsafe(replaced_lower_interval); | |
| } else if (replace_upper) { | |
| AddUnsafe(replaced_upper_interval); | |
| } | |
| CheckIntegrity(); | |
| } | |
| // Removes a single value from the interval set. | |
| void Difference(const T& value) { Difference({value, value + 1}); } | |
| // Subtracts all intervals in the other set from this one. | |
| void Difference(const IntervalSet<T>& iset) { | |
| // TODO(fangism): optimize by implementing with two advancing iterators, | |
| // like linear-time sorted-sequence set operations. | |
| for (const auto& interval : iset) { | |
| Difference(AsInterval(interval)); | |
| } | |
| } | |
| // Adds all intervals in the other set from this one. | |
| void Union(const IntervalSet<T>& iset) { | |
| // Could be optimized with a hand-written linear-merge. | |
| for (const auto& interval : iset) { | |
| Add(AsInterval(interval)); | |
| } | |
| } | |
| // Inverts the set of integers with respect to the given interval bound. | |
| void Complement(const Interval<T>& interval) { | |
| // This could be more efficient with a direct insertion of elements. | |
| IntervalSet<T> temp{{interval}}; | |
| temp.Difference(*this); | |
| swap(temp); | |
| } | |
| // Point-to-point transforms one interval set into another using | |
| // a strictly monotonic function (which may be inverting). | |
| // Interpretation of inverted interval bounds is up to the user. | |
| template <typename S> | |
| IntervalSet<S> MonotonicTransform(std::function<S(T)> func) const { | |
| IntervalSet<S> result; | |
| for (const auto& interval : intervals_) { | |
| S left = func(interval.first); | |
| S right = func(interval.second); | |
| // ignore empty intervals that may result from range compression | |
| if (left == right) continue; | |
| if (left > right) std::swap(left, right); // inverting | |
| result.AddUnsafe({left, right}); | |
| } | |
| result.CheckIntegrity(); | |
| return result; | |
| } | |
| // Returns a generator that returns a random element of the interval set, | |
| // uniformly distributed. The distribution is taken as a snapshot of the | |
| // current interval set; subsequent modifications will not affect the returned | |
| // generator; this object is safe to destroy with the generator still being | |
| // valid. | |
| std::function<T()> UniformRandomGenerator() const { | |
| struct cumulative_weighted_interval { | |
| // cumulative distribution from lowest interval to the highest interval | |
| size_t cumulative_weight; | |
| Interval<T> interval; | |
| }; | |
| // comparator for binary search | |
| auto less = [](size_t l, const cumulative_weighted_interval& r) { | |
| return l < r.cumulative_weight; | |
| }; | |
| // build cumulative distribution array for weighted random sampling | |
| std::vector<cumulative_weighted_interval> interval_map; | |
| CHECK(!empty()) << "Non-empty interval set required for random generator"; | |
| size_t cumulative_size = 0; | |
| for (const auto& range : intervals_) { | |
| const auto interval = AsInterval(range); | |
| interval_map.push_back({cumulative_size, interval}); | |
| cumulative_size += interval.length(); | |
| } | |
| // here, cumulative_size == sum_of_sizes(). | |
| return [=]() { | |
| static absl::BitGen gen; | |
| const size_t rand = absl::Uniform<size_t>(gen, 0, cumulative_size); | |
| // Convert effectively from uniform to weighted random, by interval size. | |
| // binary_search (upper_bound) is O(lg N) where N is the number | |
| // of disjoint intervals. | |
| const auto interval_iter = std::prev(std::upper_bound( | |
| interval_map.begin(), interval_map.end(), rand, less)); | |
| // rand - interval_iter->cumulative_weight can be used as the offset | |
| // into the chosen interval, which is already uniformly distributed. | |
| return interval_iter->interval.min + rand - | |
| interval_iter->cumulative_weight; | |
| }; | |
| } | |
| std::ostream& FormatInclusive(std::ostream& stream, bool compact, | |
| char delim = '-') const { | |
| return stream << absl::StrJoin( | |
| intervals_, ",", | |
| [=](std::string* out, const value_type& interval) { | |
| std::ostringstream temp_stream; | |
| AsInterval(interval).FormatInclusive(temp_stream, compact, | |
| delim); | |
| out->append(temp_stream.str()); | |
| }); | |
| } | |
| protected: | |
| // This operation is only intended for constructing test expect values. | |
| // It does not guarantee any invariants among intervals_. | |
| void AddUnsafe(const Interval<T>& interval) { | |
| CHECK(interval.valid()); | |
| CHECK(!interval.empty()); | |
| intervals_[interval.min] = interval.max; | |
| } | |
| // Checks invariant properties described in class description. | |
| void CheckIntegrity(void) const { | |
| typedef Interval<T> interval_type; | |
| if (intervals_.empty()) return; | |
| // Check front outside of loop. | |
| const_iterator iter(intervals_.begin()); | |
| const const_iterator end(intervals_.end()); | |
| { | |
| const interval_type ii(*iter); | |
| CHECK(ii.valid()); | |
| CHECK(!ii.empty()); | |
| } | |
| // Track previous max, and check the rest in loop. | |
| T prev_max = iter->second; | |
| for (++iter; iter != end; ++iter) { | |
| { | |
| const interval_type ii(*iter); | |
| CHECK(ii.valid()); | |
| CHECK(!ii.empty()); | |
| } | |
| CHECK_LT(prev_max, iter->first); | |
| prev_max = iter->second; | |
| } | |
| } | |
| // Mutable variants of Find(), LowerBound() are protected to preserve | |
| // invariants. | |
| iterator Find(const Interval<T>& interval) { | |
| return _IntervalSetImpl::FindSpanningInterval(intervals_, interval); | |
| } | |
| iterator Find(const T& value) { | |
| return _IntervalSetImpl::FindSpanningInterval(intervals_, value); | |
| } | |
| iterator LowerBound(const T& value) { | |
| return _IntervalSetImpl::FindLowerBound(intervals_, value); | |
| } | |
| iterator UpperBound(const T& value) { return intervals_.upper_bound(value); } | |
| private: | |
| // Internal storage of intervals. | |
| // Invariants: all intervals are | |
| // * non-overlapping | |
| // * non-empty | |
| // * ordered (by interval.min). | |
| impl_type intervals_; | |
| }; // class IntervalSet | |
| template <typename T> | |
| void swap(IntervalSet<T>& t1, IntervalSet<T>& t2) { | |
| t1.swap(t2); | |
| } | |
| template <typename T> | |
| std::ostream& operator<<(std::ostream& stream, const IntervalSet<T>& iset) { | |
| // Format each IntervalSet internal interval as an Interval<T>. | |
| return FormatIntervals(stream, iset.begin(), iset.end()); | |
| } | |
| // Parses a sequence of range specifications, each which can be a single value | |
| // or a range like N-M (similar to page-numbers for printing). | |
| // Overlapping/adjoining ranges are automatically merged by IntervalSet. | |
| // Iter is any iterator that points to a string (or string-like). | |
| // Returns false on any parse eror, true on complete success. | |
| template <typename T, typename Iter> | |
| bool ParseInclusiveRanges(IntervalSet<T>* iset, Iter begin, Iter end, | |
| std::ostream* errstream, const char sep = '-') { | |
| std::vector<absl::string_view> bounds; // re-use allocated memory | |
| for (const auto& range : verible::make_range(begin, end)) { | |
| bounds = absl::StrSplit(range, sep); | |
| if (bounds.size() == 1) { | |
| const auto& arg = bounds.front(); | |
| // ignore blanks, which comes from splitting "" | |
| if (arg.empty()) continue; | |
| int line_number; | |
| if (!absl::SimpleAtoi(arg, &line_number)) { | |
| *errstream << "Expected number, but got: \"" << arg << "\"." | |
| << std::endl; | |
| return false; | |
| } | |
| iset->Add(line_number); | |
| } else if (bounds.size() >= 2) { | |
| Interval<T> interval; | |
| if (!ParseInclusiveRange(&interval, bounds.front(), bounds.back(), | |
| errstream)) { | |
| return false; | |
| } | |
| iset->Add(interval); | |
| } | |
| } | |
| return true; | |
| } | |
| } // namespace verible | |
| #endif // VERIBLE_COMMON_UTIL_INTERVAL_SET_H_ |