import 'dart:math'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'value_subkey_range.freezed.dart'; part 'value_subkey_range.g.dart'; @freezed class ValueSubkeyRange with _$ValueSubkeyRange { @Assert('low >= 0 && low <= high', 'range is invalid') const factory ValueSubkeyRange({ required int low, required int high, }) = _ValueSubkeyRange; factory ValueSubkeyRange.single(int val) => ValueSubkeyRange(low: val, high: val); factory ValueSubkeyRange.make(int low, int high) => ValueSubkeyRange(low: low, high: high); factory ValueSubkeyRange.fromIntPair((int, int) pair) => ValueSubkeyRange(low: pair.$1, high: pair.$2); factory ValueSubkeyRange.fromJson(dynamic json) => _$ValueSubkeyRangeFromJson(json as Map); } extension ValueSubkeyRangeExt on ValueSubkeyRange { bool contains(int v) => low <= v && v <= high; List remove(int v) { if (v < low || v > high) { return [ValueSubkeyRange(low: low, high: high)]; } if (v == low) { if (v == high) { return []; } else { return [ValueSubkeyRange(low: v + 1, high: high)]; } } else if (v == high) { return [ValueSubkeyRange(low: low, high: v - 1)]; } else { return [ ValueSubkeyRange(low: low, high: v - 1), ValueSubkeyRange(low: v + 1, high: high) ]; } } ValueSubkeyRange? intersect(ValueSubkeyRange other) { if (high < other.low || low > other.high) { return null; } return ValueSubkeyRange( low: max(low, other.low), high: min(high, other.high)); } ValueSubkeyRange? union(ValueSubkeyRange other) { if (high < (other.low - 1) || low > (other.high + 1)) { return null; } return ValueSubkeyRange( low: min(low, other.low), high: max(high, other.high)); } } extension ListValueSubkeyRangeExt on List { static List fromIntPairs(List<(int, int)> x) => x.map(ValueSubkeyRange.fromIntPair).toList(); void validate() { int? lastHigh; for (final r in this) { assert(lastHigh == null || r.low > lastHigh, 'subrange not in order or disjoint'); lastHigh = r.high; } } bool containsSubkey(int v) => indexWhere((e) => e.contains(v)) != -1; List removeSubkey(int v) { for (var i = 0; i < length; i++) { if (this[i].contains(v)) { return [...sublist(0, i), ...this[i].remove(v), ...sublist(i + 1)]; } } return toList(); } int? get firstSubkey => isNotEmpty ? first.low : null; List intersectSubkeys(List other) { final out = []; for (var i = 0, j = 0; i < length && j < other.length;) { final vsrThis = this[i]; final vsrOther = other[j]; if (vsrThis.high < vsrOther.low) { i++; continue; } if (vsrOther.high < vsrThis.low) { j++; continue; } // Otherwise we intersect out.add(vsrThis.intersect(vsrOther)!); // Iterate whichever has a lower high // If they both have the same high then both ranges are exhausted // and should be iterated if (vsrThis.high < vsrOther.high) { // Iterate this because other could still have some overlaps i++; } else if (vsrThis.high == vsrOther.high) { // Iterate both because both ranges are exhausted i++; j++; } else { // Iterate other because this could still have some overlaps j++; } } return out; } List unionSubkeys(List other) { final out = []; ValueSubkeyRange? current; for (var i = 0, j = 0; i < length || j < other.length;) { if (i == length) { current = other[j]; j++; } else if (j == other.length) { current = this[i]; i++; } else if (this[i].low < other[j].low) { current = this[i]; i++; } else { current = other[j]; j++; } if (out.isNotEmpty && out.last.high >= (current.low - 1)) { out[out.length - 1] = ValueSubkeyRange( low: out.last.low, high: max(out.last.high, current.high)); } else { out.add(current); } } return out; } }