Skip to content

Commit

Permalink
Cleanup some comments of IntervalTree.
Browse files Browse the repository at this point in the history
  • Loading branch information
renggli committed Jan 2, 2024
1 parent 3110b23 commit babd5b3
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 19 deletions.
38 changes: 19 additions & 19 deletions lib/src/interval/tree.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import 'package:meta/meta.dart';
import 'interval.dart';

/// Immutable [IntervalTree] that can hold arbitrary elements of type [V] with
/// [Interval] of type [K]. The data structure is built in _O(n*log(n))_ and
/// can be searched in _O(log(n))_.
/// [Interval]s of type [K]. The data structure is built in _O(n*log(n))_ and
/// can be queried in _O(log(n))_.
///
/// The implementation is loosely based on the slide-deck of
/// https://www.cs.cmu.edu/~ckingsf/bioinfo-lectures/intervaltrees.pdf.
Expand All @@ -16,14 +16,14 @@ class IntervalTree<K extends Comparable<K>, V> with Iterable<V> {
fromValues<K, Interval<K>>(intervals, _identityGetter<K>);

/// Creates an [IntervalTree] form an [Iterable] of [values] and a [getter]
/// function that returns the associated interval.
/// function that returns the associated [Interval].
static IntervalTree<K, V> fromValues<K extends Comparable<K>, V>(
Iterable<V> values, Interval<K> Function(V node) getter) =>
Iterable<V> values, Interval<K> Function(V value) getter) =>
IntervalTree<K, V>._(
_IntervalTreeNode.fromValues(values, getter), getter);

/// Internal constructor of the [_IntervalTreeNode].
IntervalTree._(this._root, this.getter);
/// Internal constructor of the [IntervalTree].
const IntervalTree._(this._root, this.getter);

/// The root node of this tree, or `null` if empty.
final _IntervalTreeNode<K, V>? _root;
Expand All @@ -37,7 +37,7 @@ class IntervalTree<K extends Comparable<K>, V> with Iterable<V> {
@override
Iterator<V> get iterator => _IntervalTreeIterator(_root);

/// An [Iterable] over all values whose [Interval] intersects [value].
/// An [Iterable] over all values whose [Interval] contains [value].
Iterable<V> queryPoint(K value) sync* {
var node = _root;
while (node != null) {
Expand All @@ -64,27 +64,27 @@ class IntervalTree<K extends Comparable<K>, V> with Iterable<V> {
/// An [Iterable] over all values whose [Interval] intersects with [interval].
Iterable<V> queryInterval(Interval<K> interval) sync* {
if (_root == null) return;
final queue = <_IntervalTreeNode<K, V>>[_root];
while (queue.isNotEmpty) {
final node = queue.removeLast();
final nodes = <_IntervalTreeNode<K, V>>[_root];
while (nodes.isNotEmpty) {
final node = nodes.removeLast();
if (interval.upper.compareTo(node._median) < 0) {
for (final leftValue in node._leftValues) {
final valueInterval = getter(leftValue);
if (interval.upper.compareTo(valueInterval.lower) < 0) break;
yield leftValue;
}
if (node._leftNode != null) queue.add(node._leftNode);
if (node._leftNode != null) nodes.add(node._leftNode);
} else if (interval.lower.compareTo(node._median) > 0) {
for (final rightValue in node._rightValues) {
final valueInterval = getter(rightValue);
if (interval.lower.compareTo(valueInterval.upper) > 0) break;
yield rightValue;
}
if (node._rightNode != null) queue.add(node._rightNode);
if (node._rightNode != null) nodes.add(node._rightNode);
} else {
yield* node._leftValues;
if (node._leftNode != null) queue.add(node._leftNode);
if (node._rightNode != null) queue.add(node._rightNode);
if (node._leftNode != null) nodes.add(node._leftNode);
if (node._rightNode != null) nodes.add(node._rightNode);
}
}
}
Expand Down Expand Up @@ -123,11 +123,11 @@ final class _IntervalTreeIterator<K extends Comparable<K>, V>
@immutable
final class _IntervalTreeNode<K extends Comparable<K>, V> {
static _IntervalTreeNode<K, V>? fromValues<K extends Comparable<K>, V>(
Iterable<V> values, Interval<K> Function(V node) getter) {
Iterable<V> values, Interval<K> Function(V value) getter) {
if (values.isEmpty) return null;
final endpoints = <K>[];
for (final node in values) {
final interval = getter(node);
for (final value in values) {
final interval = getter(value);
endpoints.add(interval.lower);
endpoints.add(interval.upper);
}
Expand Down Expand Up @@ -158,13 +158,13 @@ final class _IntervalTreeNode<K extends Comparable<K>, V> {
/// All nodes smaller than the median point, or `null`.
final _IntervalTreeNode<K, V>? _leftNode;

/// All nodes containing the median point in increasing order by lower bound.
/// All values containing the median point in increasing order by lower bound.
final List<V> _leftValues;

/// The median point.
final K _median;

/// All nodes containing the median point in decreasing order by upper bound.
/// All values containing the median point in decreasing order by upper bound.
final List<V> _rightValues;

/// All nodes larger than the median point, or `null`.
Expand Down
18 changes: 18 additions & 0 deletions test/interval_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,24 @@ void main() {
expect(tree.queryInterval(Interval(2015, 2030)), [second]);
verifyInvariants(tree);
});
test('repeat', () {
final first = Interval<num>(1980, 2023);
final second = Interval<num>(1980, 2023);
final tree = IntervalTree.fromIntervals<num>([first, second]);
expect(tree, isNotEmpty);
expect(tree.length, 2);
expect(tree.isEmpty, isFalse);
expect(tree.queryPoint(1970), isEmpty);
expect(tree.queryPoint(1980), [first, second]);
expect(tree.queryPoint(2000), [first, second]);
expect(tree.queryPoint(2023), [first, second]);
expect(tree.queryPoint(2030), isEmpty);
expect(tree.queryInterval(Interval(1900, 1950)), isEmpty);
expect(tree.queryInterval(first), unorderedEquals([first, second]));
expect(tree.queryInterval(second), unorderedEquals([first, second]));
expect(tree.queryInterval(Interval(2100, 2200)), isEmpty);
verifyInvariants(tree);
});
group('stress', () {
void stress(
String name, {
Expand Down

0 comments on commit babd5b3

Please sign in to comment.