From babd5b37f2f7aed1ee6d478dba1cc5625a80a3e6 Mon Sep 17 00:00:00 2001 From: Lukas Renggli Date: Tue, 2 Jan 2024 09:33:54 +0100 Subject: [PATCH] Cleanup some comments of `IntervalTree`. --- lib/src/interval/tree.dart | 38 +++++++++++++++++++------------------- test/interval_test.dart | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/lib/src/interval/tree.dart b/lib/src/interval/tree.dart index db40c38..944c7e6 100644 --- a/lib/src/interval/tree.dart +++ b/lib/src/interval/tree.dart @@ -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. @@ -16,14 +16,14 @@ class IntervalTree, V> with Iterable { fromValues>(intervals, _identityGetter); /// 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 fromValues, V>( - Iterable values, Interval Function(V node) getter) => + Iterable values, Interval Function(V value) getter) => IntervalTree._( _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? _root; @@ -37,7 +37,7 @@ class IntervalTree, V> with Iterable { @override Iterator get iterator => _IntervalTreeIterator(_root); - /// An [Iterable] over all values whose [Interval] intersects [value]. + /// An [Iterable] over all values whose [Interval] contains [value]. Iterable queryPoint(K value) sync* { var node = _root; while (node != null) { @@ -64,27 +64,27 @@ class IntervalTree, V> with Iterable { /// An [Iterable] over all values whose [Interval] intersects with [interval]. Iterable queryInterval(Interval interval) sync* { if (_root == null) return; - final queue = <_IntervalTreeNode>[_root]; - while (queue.isNotEmpty) { - final node = queue.removeLast(); + final nodes = <_IntervalTreeNode>[_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); } } } @@ -123,11 +123,11 @@ final class _IntervalTreeIterator, V> @immutable final class _IntervalTreeNode, V> { static _IntervalTreeNode? fromValues, V>( - Iterable values, Interval Function(V node) getter) { + Iterable values, Interval Function(V value) getter) { if (values.isEmpty) return null; final endpoints = []; - 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); } @@ -158,13 +158,13 @@ final class _IntervalTreeNode, V> { /// All nodes smaller than the median point, or `null`. final _IntervalTreeNode? _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 _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 _rightValues; /// All nodes larger than the median point, or `null`. diff --git a/test/interval_test.dart b/test/interval_test.dart index 7a21476..869bf3f 100644 --- a/test/interval_test.dart +++ b/test/interval_test.dart @@ -319,6 +319,24 @@ void main() { expect(tree.queryInterval(Interval(2015, 2030)), [second]); verifyInvariants(tree); }); + test('repeat', () { + final first = Interval(1980, 2023); + final second = Interval(1980, 2023); + final tree = IntervalTree.fromIntervals([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, {