static void coalesce(Value::Ranges* ranges, const Value::Range& range) { // Note that we assume that ranges has already been coalesced. Value::Ranges result; Value::Range temp = range; for (int i = 0; i < ranges->range_size(); i++) { const Value::Range& current = ranges->range(i); // Check if current and range overlap. Note, we only need to // compare with range and not with temp to check for overlap // because we expect ranges to be coalesced to begin with! if (current.begin() <= range.end() && current.end() >= range.begin() ) { // Update temp with new boundaries. temp.set_begin(std::min(range.begin(), current.begin())); temp.set_end(std::max(range.end(), current.end())); } else { // No overlap. result.add_range()->MergeFrom(current); } } result.add_range()->MergeFrom(temp); *ranges = result; }
bool operator == (const Value::Ranges& _left, const Value::Ranges& _right) { Value::Ranges left; coalesce(&left, _left); Value::Ranges right; coalesce(&right, _right); if (left.range_size() == right.range_size()) { for (int i = 0; i < left.range_size(); i++) { // Make sure this range is equal to a range in the right. bool found = false; for (int j = 0; j < right.range_size(); j++) { if (left.range(i).begin() == right.range(j).begin() && left.range(i).end() == right.range(j).end()) { found = true; break; } } if (!found) { return false; } } return true; } return false; }
// Coalesce the given un-coalesced 'uranges' into already coalesced 'ranges'. static void coalesce(Value::Ranges* ranges, const Value::Ranges& uranges) { // Note that we assume that ranges has already been coalesced. for (int i = 0; i < uranges.range_size(); i++) { coalesce(ranges, uranges.range(i)); } }
Value::Ranges operator - (const Value::Ranges& left, const Value::Ranges& right) { Value::Ranges result; coalesce(&result, left); coalesce(&result, right); for (int i = 0; i < right.range_size(); i++) { remove(&result, right.range(i)); } return result; }
bool operator <= (const Value::Ranges& _left, const Value::Ranges& _right) { Value::Ranges left; coalesce(&left, _left); Value::Ranges right; coalesce(&right, _right); for (int i = 0; i < left.range_size(); i++) { // Make sure this range is a subset of a range in right. bool matched = false; for (int j = 0; j < right.range_size(); j++) { if (left.range(i).begin() >= right.range(j).begin() && left.range(i).end() <= right.range(j).end()) { matched = true; break; } } if (!matched) { return false; } } return true; }
// Subtract `right_` from `left_`, and return the result as Value::Ranges. Value::Ranges subtract(const Value::Ranges& left_, const Value::Ranges& right_) { if (left_.range_size() == 0 || right_.range_size() == 0) { return left_; } // Convert the input `Ranges` to `vector<internal::Range>` and // sort the vector based on the start of a range. auto sortRanges = [](const Value::Ranges& ranges) { vector<internal::Range> result; result.reserve(ranges.range_size()); foreach (const Value::Range& range, ranges.range()) { result.push_back({range.begin(), range.end()}); } std::sort( result.begin(), result.end(), [](const internal::Range& left, const internal::Range& right) { return left.start < right.start; }); return result; }; Value::Ranges result; vector<internal::Range> left = sortRanges(left_); vector<internal::Range> right = sortRanges(right_); vector<internal::Range>::iterator itLeft = left.begin(); for (vector<internal::Range>::const_iterator itRight = right.cbegin(); itLeft != left.end() && itRight != right.cend();) { // Non-overlap: // L: |___| // R: |___| if (itLeft->end < itRight->start) { Value::Range* newRange = result.add_range(); newRange->set_begin(itLeft->start); newRange->set_end(itLeft->end); itLeft++; continue; } // Non-overlap: // L: |___| // R: |___| if (itLeft->start > itRight->end) { itRight++; continue; } if (itLeft->start < itRight->start) { Value::Range* newRange = result.add_range(); newRange->set_begin(itLeft->start); newRange->set_end(itRight->start - 1); if (itLeft->end <= itRight->end) { // L: |_____| // R: |____| itLeft++; } else { // L: |________| // R: |___| itLeft->start = itRight->end + 1; itRight++; } } else { // itLeft->start >= itRight->start if (itLeft->end <= itRight->end) { // L: |____| // R: |________| itLeft++; } else { // L: |_____| // R: |____| itLeft->start = itRight->end + 1; itRight++; } } } // Traverse what's left in the `left`, if any. while (itLeft != left.end()) { // TODO(mzhu): Consider reserving the exact size. Value::Range* newRange = result.add_range(); newRange->set_begin(itLeft->start); newRange->set_end(itLeft->end); itLeft++; } return result; }