Skip to content

Commit

Permalink
More docs and examples
Browse files Browse the repository at this point in the history
- added more docs
- example for defining custom `Refs`
- macro for defining custom `Refs`
  • Loading branch information
aslpavel committed Aug 16, 2018
1 parent 45fdd09 commit 20b4168
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 50 deletions.
6 changes: 3 additions & 3 deletions benches/criterion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fn bench_split(c: &mut Criterion) {
let ft: rc::FingerTree<_> = (0..1024).map(Size).collect();
c.bench_function_over_inputs(
"split",
move |b, &size| b.iter(|| ft.split(|m| m.value > *size)),
move |b, &size| b.iter(|| ft.split(|m| **m > *size)),
SPLIT_1024,
);
}
Expand All @@ -31,7 +31,7 @@ fn bench_concat(c: &mut Criterion) {
let ft: rc::FingerTree<_> = (0..1024).map(Size).collect();
let ft_split: HashMap<_, _> = SPLIT_1024
.iter()
.map(|size| (size, ft.split(|m| m.value > *size)))
.map(|size| (size, ft.split(|m| **m > *size)))
.collect();

c.bench_function_over_inputs(
Expand All @@ -52,7 +52,7 @@ fn bench_arc_vs_rc(c: &mut Criterion) {
let (size, split) = i;
let ft: FingerTree<R, _> = (0..*size).map(Size).collect();
b.iter(|| {
let (left, right) = ft.split(|m| m.value > *split);
let (left, right) = ft.split(|m| **m > *split);
left.concat(&right)
})
}
Expand Down
22 changes: 13 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@
//! Finger trees is a functional representation of persistent sequences
//! supporting access to the ends in amortized constant time, and concatenation
//! and splitting in time logarithmic in the size of the smaller piece. It also
//! has `split` operation defined in general form, which can be used to implement
//! sequence, priority queue, search tree, priority search queue and more
//! datastructures.
//! has [`split`](struct.FingerTree.html#method.split) operation defined in general
//! form, which can be used to implement sequence, priority queue, search tree,
//! priority search queue and more datastructures.
//!
//! ## Links:
//! - Original paper: [Finger Trees: A Simple General-purpose Data Structure](http://www.staff.city.ac.uk/~ross/papers/FingerTree.html)
//! - Wikipedia article: [FingerTree](https://en.wikipedia.org/wiki/Finger_tree)
//!
//! ## Notes:
//! - This implementation does not use non-regular recursive types as implementation
//! described in a paper. As rust's monomorphization does not play well with such types.
//! described in the paper. As rust's monomorphization does not play well with such types.
//! - Implmentation abstracts over reference counted types `Rc/Arc`. Using type family trick.
//! - Uses strict spine in implementation.
//! - Iterator returns cloned value, and in general this implementation assumes that value
Expand All @@ -24,22 +24,24 @@
//!
//! ## Examples:
//! ```
//! # extern crate fingertree;
//! # use std::iter::FromIterator;
//! use fingertree::measure::Size;
//! use fingertree::monoid::Sum;
//! use fingertree::{FingerTree, Measured, RcRefs};
//!
//! // construct finger tree with `Size` measure
//! // construct `Rc` based finger tree with `Size` measure
//! let ft: FingerTree<RcRefs, _> = vec!["one", "two", "three", "four", "five"]
//! .into_iter()
//! .map(Size)
//! .collect();
//! assert_eq!(ft.measure(), Sum(5));
//!
//! // split with predicate
//! let (left, right) = ft.split(|m| m.value > 2);
//! assert_eq!(left.measure(), Sum::new(2));
//! let (left, right) = ft.split(|measure| *measure > Sum(2));
//! assert_eq!(left.measure(), Sum(2));
//! assert_eq!(Vec::from_iter(&left), vec![Size("one"), Size("two")]);
//! assert_eq!(right.measure(), Sum::new(3));
//! assert_eq!(right.measure(), Sum(3));
//! assert_eq!(Vec::from_iter(&right), vec![Size("three"), Size("four"), Size("five")]);
//!
//! // concatinate
Expand All @@ -54,6 +56,7 @@
//! .collect(),
//! );
//! ```
#![doc(test(no_crate_inject))]
#![deny(missing_docs)]
#![deny(warnings)]

Expand All @@ -73,8 +76,9 @@ mod test;

pub use measure::Measured;
pub use monoid::Monoid;
pub use node::NodeInner;
pub use reference::{ArcRefs, RcRefs, Ref, Refs};
pub use tree::FingerTree;
pub use tree::{FingerTree, FingerTreeInner};

pub mod rc {
//! `Rc` based implementation of `FingerTree`
Expand Down
5 changes: 3 additions & 2 deletions src/measure.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! `Measured` trait and definations
//! [`Measured`](measure/trait.Measured.html) trait and implementations
use std::fmt;
use std::ops::Deref;

Expand Down Expand Up @@ -49,12 +49,13 @@ where
type Measure = Sum<usize>;

fn measure(&self) -> Self::Measure {
Sum::new(1)
Sum(1)
}
}

impl<T> Deref for Size<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
&self.0
}
Expand Down
26 changes: 9 additions & 17 deletions src/monoid.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! `Monoid` trait and implementations
//! [`Monoid`](monoid/trait.Monoid.html) trait and implementations
use std::ops::{Add, Deref};

/// Monoid definition
Expand All @@ -12,7 +12,7 @@ use std::ops::{Add, Deref};
/// - **associativity**: `a + (b + c) == (a + b) + c`
/// - **identity element**: `zero + a == a + zero == a`
pub trait Monoid {
/// `zero` or `identity` elemnt of monoid
/// `zero` or `identity` element of monoid
fn zero() -> Self;
/// `plus` operation of `Monoid`
fn plus(&self, other: &Self) -> Self;
Expand All @@ -33,36 +33,28 @@ pub trait Monoid {
// }

/// Monoid formed by `Add::add` operation and `Default::default()` identity element
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Sum<T> {
/// New type wrapped value
pub value: T,
}

impl<T> Sum<T> {
/// Create `Sum` monoid from any value
pub fn new(value: T) -> Self {
Sum { value }
}
}
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct Sum<T>(pub T);

impl<T> Monoid for Sum<T>
where
for<'a> &'a T: Add<Output = T>,
T: Default,
{
fn zero() -> Self {
Sum::new(T::default())
Sum(T::default())
}

fn plus(&self, other: &Self) -> Self {
Sum::new(&self.value + &other.value)
Sum(&self.0 + &other.0)
}
}

impl<T> Deref for Sum<T> {
type Target = T;

#[inline]
fn deref(&self) -> &Self::Target {
&self.value
&self.0
}
}
4 changes: 4 additions & 0 deletions src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@ use measure::Measured;
use monoid::Monoid;
use reference::{Ref, Refs};

/// Only visible to defne custom [`Refs`](trait.Refs.html)
pub enum NodeInner<R, V>
where
R: Refs<V>,
V: Measured,
{
#[doc(hidden)]
Leaf(V),
#[doc(hidden)]
Node2 {
measure: V::Measure,
left: Node<R, V>,
right: Node<R, V>,
},
#[doc(hidden)]
Node3 {
measure: V::Measure,
left: Node<R, V>,
Expand Down
91 changes: 76 additions & 15 deletions src/reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,64 @@ where
fn new(value: Self::Target) -> Self;
}

/// Interface which defines all reference types needed by finger tree
/// implementation.
impl<T> Ref for Rc<T> {
fn new(value: Self::Target) -> Self {
Rc::new(value)
}
}

impl<T> Ref for Arc<T> {
fn new(value: Self::Target) -> Self {
Arc::new(value)
}
}

/// Interface which defines all reference types needed by finger tree implementation.
///
/// By implementing this interface for your reference type you can use finger treee
/// with your reference type.
///
/// # Example:
/// ```
/// #[macro_use]
/// extern crate fingertree;
///
/// use std::rc::Rc;
/// use std::ops::Deref;
/// use fingertree::measure::Size;
/// use fingertree::{FingerTree, Measured, Ref};
///
/// // your custom reference type
/// struct MyRef<T>(Rc<T>);
///
/// impl<T> Clone for MyRef<T> {
/// fn clone(&self) -> Self {
/// MyRef(self.0.clone())
/// }
/// }
///
/// impl<T> Deref for MyRef<T> {
/// type Target = T;
/// fn deref(&self) -> &T {
/// &*self.0
/// }
/// }
///
/// impl<T> Ref for MyRef<T>
/// {
/// fn new(value: T) -> Self {
/// MyRef(Rc::new(value))
/// }
/// }
///
/// // define type family for your reference
/// fingertree_define_refs!(MyRefs, MyRef);
///
/// # fn main() {
/// // now you can construct fingertree using your reference type
/// let _: FingerTree<MyRefs, _> = (0..128).map(Size).collect();
/// # }
/// ```
pub trait Refs<V>: Sized
where
V: Measured,
Expand All @@ -27,26 +83,31 @@ where
type Tree: Ref<Target = FingerTreeInner<Self, V>>;
}

macro_rules! define_refs {
($ref:ident, $refs:ident) => {
impl<T> $crate::reference::Ref for $ref<T> {
fn new(value: T) -> Self {
$ref::new(value)
}
}

/// Helper macro to define custom [`Refs`](trait.Refs.html) for `FingerTree`
#[macro_export]
macro_rules! fingertree_define_refs {
(pub $refs:ident, $ref:ident) => {
/// References type family
pub enum $refs {}
fingertree_define_refs!(@refs_impl $refs, $ref);
};

($refs:ident, $ref:ident) => {
/// References type family
enum $refs {}
fingertree_define_refs!(@refs_impl $refs, $ref);
};

impl<V> $crate::reference::Refs<V> for $refs
(@refs_impl $refs:ident, $ref:ident) => {
impl<V> $crate::Refs<V> for $refs
where
V: $crate::measure::Measured,
{
type Node = $ref<$crate::node::NodeInner<Self, V>>;
type Tree = $ref<$crate::tree::FingerTreeInner<Self, V>>;
type Node = $ref<$crate::NodeInner<Self, V>>;
type Tree = $ref<$crate::FingerTreeInner<Self, V>>;
}
};
}

define_refs!(Rc, RcRefs);
define_refs!(Arc, ArcRefs);
fingertree_define_refs!(pub RcRefs, Rc);
fingertree_define_refs!(pub ArcRefs, Arc);
2 changes: 1 addition & 1 deletion src/test/quickcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ quickcheck! {
fn split_and_concat(ft: FingerTree<Size<i32>>, index: usize) -> bool {
let len = *ft.measure();
let index = if len != 0 { index % len } else { 0 };
let (left, right) = ft.split(|m| m.value > index);
let (left, right) = ft.split(|m| **m > index);
validate(&left);
validate(&right);
true
Expand Down
2 changes: 1 addition & 1 deletion src/test/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ fn concat() {
fn split() {
let ft: RcFingerTree<_> = (0..TEST_SIZE).map(Size).collect();
for split in 0..TEST_SIZE {
let (left, right) = ft.split(|m| m.value > split);
let (left, right) = ft.split(|m| **m > split);
validate(&left);
validate(&right);
assert_eq!(*left.measure(), split);
Expand Down
10 changes: 8 additions & 2 deletions src/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ use monoid::Monoid;
use node::{Node, NodeInner};
use reference::{Ref, Refs};

/// Only visible to defne custom [`Refs`](trait.Refs.html)
pub enum FingerTreeInner<R, V>
where
R: Refs<V>,
V: Measured,
{
#[doc(hidden)]
Empty,
#[doc(hidden)]
Single(Node<R, V>),
#[doc(hidden)]
Deep {
measure: V::Measure,
left: Digit<Node<R, V>>,
Expand Down Expand Up @@ -348,10 +352,12 @@ where
}
}

/// FingerTree implemenetation
///
/// FingerTree is parametrized by two type parpameters
/// - `R` - type family trick which determines type of references used in
/// implementation. This crate implementes [`ArcRefs`](::reference::ArcRefs) which is based
/// on `Arc` atomic reference counter, and [`RcRefs`] which is based
/// implementation. This crate implementes [`ArcRefs`](enum.ArcRefs.html) which is based
/// on `Arc` atomic reference counter, and [`RcRefs`](enum.RcRefs.html) which is based
/// on `Rc`.
/// - `V` - value type which must be measurable and cheaply clonable.
pub struct FingerTree<R, V>
Expand Down

0 comments on commit 20b4168

Please sign in to comment.