Skip to content

Commit

Permalink
Tweak docs again..
Browse files Browse the repository at this point in the history
  • Loading branch information
nielsenko committed Dec 8, 2023
1 parent bd879ff commit d4d4161
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 95 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

# Conventional directory for build outputs.
build/
doc/

# Omit committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
Expand Down
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
## 1.0.0
## 0.1.0

- Initial version.
- Initial version. Public interface still subject to change.
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
A library implementing a persistent treap data structure for Dart developers.
A package implementing a persistent treap data structure.

## Usage

Expand All @@ -16,8 +16,8 @@ main() {
final x = {for (int i = 0; i < max; ++i) rnd.nextInt(max)};
final y = {for (int i = 0; i < max; ++i) rnd.nextInt(max)};
final tx = Treap<num>.build(x);
final ty = Treap<num>.build(y);
final tx = Treap<num>.of(x);
final ty = Treap<num>.of(y);
expect((tx | ty).values, x.union(y));
expect((tx & ty).values, x.intersection(y));
Expand All @@ -27,7 +27,11 @@ main() {
}
```

For something more significant see the [todo](example/) flutter app
For something more substantial see the [todo](example/) flutter app.

It illustrates how to use a persistent treap to handle undo/redo and animate a list view.

If your browser supports WasmGC (such as Chrome 119+, or Firefox 120+), you can try out the app [here](https://byolimit.github.io)

## Status

Expand Down
22 changes: 11 additions & 11 deletions lib/src/node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ extension NodeEx<T extends Comparable<T>> on Node<T>? {
int get size => this?._size ?? 0;

Split<T> split(T pivot) {
final self = this;
final self = this; // for type promotion
if (self == null) return emptySplit<T>();
final order = pivot.compareTo(self.item);
if (order < 0) {
Expand All @@ -92,10 +92,10 @@ extension NodeEx<T extends Comparable<T>> on Node<T>? {
}

Node<T>? join(Node<T>? other) {
final self = this;
final self = this; // for type promotion
if (self != null && other != null) {
assert(self.max.compareTo(other.min) < 0);
// two - ensure heap order
// two nodes - ensure heap order
if (self.priority > other.priority) {
return self.withRight(self.right.join(other));
}
Expand All @@ -109,7 +109,7 @@ extension NodeEx<T extends Comparable<T>> on Node<T>? {
Node<T>? erase(T dead) => split(dead).withMiddle(null).join();

Node<T>? union(Node<T>? other) {
final self = this;
final self = this; // for type promotion
if (self == null) return other; // {} | B == B
final s = other.split(self.item);
return newSplit(
Expand All @@ -120,7 +120,7 @@ extension NodeEx<T extends Comparable<T>> on Node<T>? {
}

Node<T>? intersection(Node<T>? other) {
final self = this;
final self = this; // for type promotion
if (self == null || other == null) return null; // {} & B == A & {} == {}
final s = other.split(self.item);
return newSplit(
Expand All @@ -131,7 +131,7 @@ extension NodeEx<T extends Comparable<T>> on Node<T>? {
}

Node<T>? difference(Node<T>? other) {
final self = this;
final self = this; // for type promotion
if (self == null) return null; // {} - B == {}
if (other == null) return self; // A - {} == A
final s = other.split(self.item);
Expand All @@ -143,7 +143,7 @@ extension NodeEx<T extends Comparable<T>> on Node<T>? {
}

Node<T>? find(T item) {
final self = this;
final self = this; // for type promotion
if (self == null) return null;
final order = item.compareTo(self.item);
if (order < 0) return self.left?.find(item);
Expand All @@ -152,7 +152,7 @@ extension NodeEx<T extends Comparable<T>> on Node<T>? {
}

int rank(T item) {
final self = this;
final self = this; // for type promotion
if (self == null) return 0;
final order = item.compareTo(self.item);
if (order < 0) return self.left.rank(item);
Expand All @@ -161,9 +161,9 @@ extension NodeEx<T extends Comparable<T>> on Node<T>? {
return l; // order == 0
}

/// throws if out of bounds
/// Throws a [RangeError] if [rank] is out of bounds
Node<T> select(int rank) {
final self = this;
final self = this; // for type promotion
if (self == null || rank < 0 || rank >= size) {
throw RangeError.range(rank, 0, size - 1, 'rank');
}
Expand All @@ -174,7 +174,7 @@ extension NodeEx<T extends Comparable<T>> on Node<T>? {
}

Iterable<T> get values {
final self = this;
final self = this; // for type promotion
if (self == null) return [];
return self.inOrder().map((n) => n.item);
}
Expand Down
178 changes: 115 additions & 63 deletions lib/src/treap_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,114 +4,166 @@ import 'node.dart';

final _rnd = Random(42);

/// Treap class
/// A treap is a type of binary search tree data structure that maintains a dynamic
/// set of ordered keys and allows binary search tree operations in addition to operations
/// like add, find, and erase in O(log n) time. The name 'Treap' is a portmanteau
/// of tree and heap, as the tree maintains its shape using heap properties.
/// A [fully persistent](https://en.wikipedia.org/wiki/Persistent_data_structure)
/// (immutable) implementation of a [Treap](https://en.wikipedia.org/wiki/Treap).
///
/// A treap is a type of binary search tree. Each node in the tree is assigned a
/// uniformly distributed priority, either randomly or by a strong hash.
///
/// Nodes in the tree are kept heap-ordered wrt. their priority. This ensures that
/// the shape of the tree is a random variable with the same probability distribution
/// as a random binary tree. Hence the name treap, which is a portmanteau of tree
/// and heap.
///
/// In particular (with high probability), given a treap with `N` keys, the height
/// is `O(log(N))`, so that [find], [add], [erase], etc. also takes `O(log(N))` time
/// to perform.
///
/// This particular implementation is made [persistent](https://en.wikipedia.org/wiki/Persistent_data_structure),
/// by means of path copying.
///
/// Both [add] and [erase] has a space complexity of `O(log(N))`, due to path copying,
/// but erased nodes can later be reclaimed by the garbage collector, if the old
/// treaps containing them becomes eligible for reaping.
class Treap<T extends Comparable<T>> {
final Node<T>? _root;

const Treap._(this._root);
const Treap() : this._(null);

/// The build method takes an iterable of items and constructs a treap from them.
/// It does this by folding over the items, adding each one to the treap in turn.
/// This method is O(N log(N)) in complexity. An O(N) algorithm exists if the items
/// are sorted. However, this method is simpler and works even if the items are not
/// sorted.
factory Treap.build(Iterable<T> items) =>
items.fold(Treap(), (acc, i) => acc.add(i));

/// Adds an item to the treap. Creates a new node with the item and a random priority.
/// The new node is then added to the root of the treap. Returns a new treap with the
/// added node.
/// The [Comparator] used to determine element order.
///
/// Defaults to [Comparable.compare].
final Comparator<T> comparator;

const Treap._(this._root, this.comparator);

/// The empty [Treap] for a given [comparator].
const Treap([Comparator<T>? comparator])
: this._(null, comparator ?? Comparable.compare);

/// Build a treap containing the [items].
///
/// Constructs the treap by folding [add] over the [items], adding each one
/// to the treap in turn.
///
/// This method is `O(N log(N))` in complexity. An `O(N)` algorithm exists if the
/// items are sorted. However, this works in all cases.
factory Treap.of(Iterable<T> items, [Comparator<T>? comparator]) =>
items.fold(Treap(comparator), (acc, i) => acc.add(i));

/// Adds an [item].
///
/// Creates a new node with the [item] and a random priority. Returns a new treap
/// with the added node.
Treap<T> add(T item) =>
Treap<T>._(_root.add(Node<T>(item, _rnd.nextInt(1 << 32))));
Treap<T>._(_root.add(Node<T>(item, _rnd.nextInt(1 << 32))), comparator);

/// Erases an item from the treap. Returns a new treap without the erased item.
Treap<T> erase(T item) => Treap._(_root.erase(item));
/// Adds a range of [items].
///
/// Returns a new treap with the added [items].
Treap<T> addRange(Iterable<T> items) => union(Treap.of(items, comparator));

/// Checks if the treap is empty. Returns true if the treap is empty, false otherwise.
/// Erases an [item] from the treap, if it exists.
///
/// Returns a new treap without the erased [item].
Treap<T> erase(T item) => Treap._(_root.erase(item), comparator);

/// Whether this treap is empty.
bool get isEmpty => _root == null;

/// Returns the size of the treap.
/// The size of this treap.
int get size => _root.size;

/// Finds an item in the treap. Returns the item if found, null otherwise.
/// Finds the [item] in this treap.
///
/// Returns the [T] in the treap, if any, that orders together with [item] by [comparator].
/// Otherwise returns `null`.
T? find(T item) => _root.find(item)?.item;

/// Checks if an item exists in the treap. Returns true if the item exists, false
/// otherwise.
/// Whether an[item] exists in this treap.
///
/// Returns `true` if `find` re
bool has(T item) => find(item) != null;

/// Returns the rank of an item in the treap.
/// The rank of an [item].
///
/// For an [item] in this treap, the rank is the index of the item. For an item not
/// in this treap, the rank is the index it would be at, if it was added.
int rank(T item) => _root.rank(item);

/// Selects an item in the treap by its rank. Returns the selected item.
T select(int rank) => _root.select(rank).item;
/// Selects an item in this treap by its [index].
T select(int index) => _root.select(index).item;

/// Returns all the values in the treap.
/// The values in this treap ordered by the [comparator].
Iterable<T> get values => _root.values;

/// Returns the first item in the treap, or null if the treap is empty.
/// The first item in this treap, or `null` if it is empty.
T? get firstOrDefault => _root?.min;

/// Returns the last item in the treap, or null if the treap is empty.
/// The last item in this treap, or `null` if it is empty.
T? get lastOrDefault => _root?.max;

/// Returns the first item in the treap. Throws an error if the treap is empty.
/// The first item in this treap.
///
/// Throws a [StateError] if it is empty.
T get first => firstOrDefault ?? (throw StateError('No element'));

/// Returns the last item in the treap. Throws an error if the treap is empty.
/// The last item in this treap.
///
/// Throws a [StateError] if it is empty.
T get last => lastOrDefault ?? (throw StateError('No element'));

/// Returns the previous item in the treap for a given item.
/// The item preceding a given [item] in this treap.
///
/// Throws a [RangeError] if no such item exists. Note that [item] need not be
/// contained this treap.
T prev(T item) => _root.select(rank(item) - 1).item;

/// Returns the next item in the treap for a given item.
/// Returns the next item in the treap for a given [item].
///
/// [item] need not be contained this treap.
/// Throws a [RangeError] if no such item exists.
T next(T item) => _root.select(rank(item) + 1).item;

/// Returns a new treap with the first n items. If n is greater than the size of
/// the treap, returns the original treap.
Treap<T> take(int n) =>
n < size ? Treap._(_root.split(_root.select(n).item).low) : this;

/// Skips the first n items and returns a new treap with the remaining items.
/// If n is greater than or equal to the size of the treap, returns an empty treap.
/// Returns a new treap with the first [n] items.
///
/// Returns the original treap, if [n] is greater than the [size] of this treap.
Treap<T> take(int n) => n < size
? Treap._(_root.split(_root.select(n).item).low, comparator)
: this;

/// Skips the first [n] items and returns a new treap with the remaining items.
///
/// Returns an empty treap, if [n] is greater than or equal to the [size] of this
/// treap.
Treap<T> skip(int n) {
if (n >= size) return Treap(); // empty
if (n >= size) return Treap(comparator); // empty
final split = _root.split(_root.select(n).item);
return Treap._(split.high.union(split.middle));
return Treap._(split.high.union(split.middle), comparator);
}

/// Returns a new treap that is the union of this treap and another treap.
Treap<T> union(Treap<T> other) => Treap._(_root.union(other._root));
/// Returns a new treap that is the union of this treap and the [other] treap.
Treap<T> union(Treap<T> other) =>
Treap._(_root.union(other._root), comparator);

/// Returns a new treap that is the intersection of this treap and another treap.
/// Returns a new treap that is the intersection of this treap and the [other] treap.
Treap<T> intersection(Treap<T> other) =>
Treap._(_root.intersection(other._root));
Treap._(_root.intersection(other._root), comparator);

/// Returns a new treap that is the difference of this treap and another treap.
Treap<T> difference(Treap<T> other) => Treap._(_root.difference(other._root));
/// Returns a new treap that is the difference of this treap minus the [other] treap.
Treap<T> difference(Treap<T> other) =>
Treap._(_root.difference(other._root), comparator);

/// Operator overloading for adding an item to the treap. Returns a new treap with
/// the added item.
/// Operator overload for [add]ing an [item] to the treap.
Treap<T> operator +(T item) => add(item);

/// Operator overloading for the union of this treap and another treap.
/// Returns a new treap that is the union of the two treaps.
/// Operator overload for the [union] of two treaps.
Treap<T> operator |(Treap<T> other) => union(other);

/// Operator overloading for the intersection of this treap and another treap.
/// Returns a new treap that is the intersection of the two treaps.
/// Operator overload for the [intersection] of two treaps.
Treap<T> operator &(Treap<T> other) => intersection(other);

/// Operator overloading for the difference of this treap and another treap.
/// Returns a new treap that is the difference of the two treaps.
/// Operator overload for the [difference] of two treaps.
Treap<T> operator -(Treap<T> other) => difference(other);

/// Operator overloading for selecting an item in the treap by its rank.
/// Returns the selected item.
T operator [](int i) => select(i);
/// Operator overload for [select]ing an item in the treap by its [index].
T operator [](int index) => select(index);
}
8 changes: 7 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ description: >-
A heap balanced randomized binary tree with efficient value semantics.
All operations are O(log n) worst case.
version: 1.0.0
topics:
- treap
- data-structures
- collections
- functional

version: 0.1.0 # still some planned interface changes
homepage: https://github.com/nielsenko/treap

environment:
Expand Down
Loading

0 comments on commit d4d4161

Please sign in to comment.