blob: a1b56ea1bae5c88327ccc089d889eef7dd05eb1c [file] [log] [blame] [edit]
//! Crate `slice-group-by` is a library for efficiently iterating on a slice by groups defined by
//! a function that specifies if two elements are in the same group.
//!
//! # Example: Linear Searched Immutable Groups
//!
//! You will only need to define a function that returns `true` if two elements are in the same group.
//!
//! The `LinearGroupBy` iterator will always gives contiguous elements to the predicate function.
//!
//! ```rust
//! use slice_group_by::GroupBy;
//!
//! let slice = &[1, 1, 1, 3, 3, 2, 2, 2];
//!
//! let mut iter = slice.linear_group_by_key(|x| -x);
//!
//! assert_eq!(iter.next(), Some(&[1, 1, 1][..]));
//! assert_eq!(iter.next(), Some(&[3, 3][..]));
//! assert_eq!(iter.next(), Some(&[2, 2, 2][..]));
//! assert_eq!(iter.next(), None);
//! ```
//!
//! # Example: Linear Searched Immutable Str Slices
//!
//! You will only need to define a function that returns `true` if two `char` are in the same group.
//!
//! The `LinearStrGroupBy` iterator will always gives contiguous `char` to the predicate function.
//!
//! ```rust
//! use slice_group_by::StrGroupBy;
//!
//! let string = "aaaabbbbb饰饰cccc";
//!
//! let mut iter = string.linear_group_by(|a, b| a == b);
//!
//! assert_eq!(iter.next(), Some("aaaa"));
//! assert_eq!(iter.next(), Some("bbbbb"));
//! assert_eq!(iter.next(), Some("饰饰"));
//! assert_eq!(iter.next(), Some("cccc"));
//! assert_eq!(iter.next(), None);
//! ```
//!
//! # Example: Binary Searched Mutable Groups
//!
//! It is also possible to get mutable non overlapping groups of a slice.
//!
//! The `BinaryGroupBy/Mut` and `ExponentialGroupBy/Mut` iterators will not necessarily
//! gives contiguous elements to the predicate function. The predicate function should implement
//! an order consistent with the sort order of the slice.
//!
//! ```rust
//! use slice_group_by::GroupByMut;
//!
//! let slice = &mut [1, 1, 1, 2, 2, 2, 3, 3];
//!
//! let mut iter = slice.binary_group_by_mut(|a, b| a == b);
//!
//! assert_eq!(iter.next(), Some(&mut [1, 1, 1][..]));
//! assert_eq!(iter.next(), Some(&mut [2, 2, 2][..]));
//! assert_eq!(iter.next(), Some(&mut [3, 3][..]));
//! assert_eq!(iter.next(), None);
//! ```
//!
//! # Example: Exponential Searched Mutable Groups starting from the End
//!
//! It is also possible to get mutable non overlapping groups of a slice even starting from the end of it.
//!
//! ```rust
//! use slice_group_by::GroupByMut;
//!
//! let slice = &mut [1, 1, 1, 2, 2, 2, 3, 3];
//!
//! let mut iter = slice.exponential_group_by_mut(|a, b| a == b).rev();
//!
//! assert_eq!(iter.next(), Some(&mut [3, 3][..]));
//! assert_eq!(iter.next(), Some(&mut [2, 2, 2][..]));
//! assert_eq!(iter.next(), Some(&mut [1, 1, 1][..]));
//! assert_eq!(iter.next(), None);
//! ```
//!
#![cfg_attr(feature = "nightly", feature(ptr_offset_from))]
#![cfg_attr(feature = "nightly", feature(test))]
#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
#[cfg(all(not(test), not(feature = "std")))]
extern crate core as std;
macro_rules! group_by_wrapped {
(struct $name:ident, $elem:ty) => {
impl<'a, T: 'a> std::iter::Iterator for $name<'a, T>
where T: PartialEq,
{
type Item = $elem;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
fn last(self) -> Option<Self::Item> {
self.0.last()
}
}
impl<'a, T: 'a> DoubleEndedIterator for $name<'a, T>
where T: PartialEq,
{
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back()
}
}
impl<'a, T: 'a> std::iter::FusedIterator for $name<'a, T>
where T: PartialEq,
{ }
}
}
mod linear_group;
mod binary_group;
mod exponential_group;
mod linear_str_group;
use std::cmp::{self, Ordering};
pub use self::linear_group::{
LinearGroupByKey,
LinearGroupBy,
LinearGroup,
LinearGroupByKeyMut,
LinearGroupByMut,
LinearGroupMut,
};
pub use self::binary_group::{
BinaryGroupByKey,
BinaryGroupBy,
BinaryGroup,
BinaryGroupByKeyMut,
BinaryGroupByMut,
BinaryGroupMut,
};
pub use self::exponential_group::{
ExponentialGroupByKey,
ExponentialGroupBy,
ExponentialGroup,
ExponentialGroupByKeyMut,
ExponentialGroupByMut,
ExponentialGroupMut,
};
pub use self::linear_str_group::{
LinearStrGroupByKey,
LinearStrGroupBy,
LinearStrGroup,
LinearStrGroupByKeyMut,
LinearStrGroupByMut,
LinearStrGroupMut,
};
#[cfg(feature = "nightly")]
#[inline]
unsafe fn offset_from<T>(to: *const T, from: *const T) -> usize {
to.offset_from(from) as usize
}
#[cfg(not(feature = "nightly"))]
#[inline]
unsafe fn offset_from<T>(to: *const T, from: *const T) -> usize {
use std::mem;
(to as usize - from as usize) / mem::size_of::<T>()
}
/// Exponential searches this sorted slice for a given element.
///
/// If the value is found then `Ok` is returned, containing the index of the matching element;
/// if the value is not found then `Err` is returned, containing the index where a matching element
/// could be inserted while maintaining sorted order.
///
/// # Examples
///
/// Looks up a series of four elements. The first is found, with a
/// uniquely determined position; the second and third are not
/// found; the fourth could match any position in `[1, 4]`.
///
/// ```
/// use slice_group_by::exponential_search;
///
/// let s = &[0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
///
/// assert_eq!(exponential_search(s, &13), Ok(9));
/// assert_eq!(exponential_search(s, &4), Err(7));
/// assert_eq!(exponential_search(s, &100), Err(13));
/// let r = exponential_search(s, &1);
/// assert!(match r { Ok(1..=4) => true, _ => false, });
/// ```
#[inline]
pub fn exponential_search<T>(slice: &[T], elem: &T) -> Result<usize, usize>
where T: Ord
{
exponential_search_by(slice, |x| x.cmp(elem))
}
/// Binary searches this sorted slice with a comparator function.
///
/// The comparator function should implement an order consistent with the sort order of
/// the underlying slice, returning an order code that indicates whether its argument
/// is `Less`, `Equal` or `Greater` the desired target.
///
/// If the value is found then `Ok` is returned, containing the index of the matching element;
/// if the value is not found then `Err` is returned, containing the index where a matching element
/// could be inserted while maintaining sorted order.
///
/// # Examples
///
/// Looks up a series of four elements. The first is found, with a
/// uniquely determined position; the second and third are not
/// found; the fourth could match any position in `[1, 4]`.
///
/// ```
/// use slice_group_by::exponential_search_by;
///
/// let s = &[0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55];
///
/// let seek = 13;
/// assert_eq!(exponential_search_by(s, |probe| probe.cmp(&seek)), Ok(9));
/// let seek = 4;
/// assert_eq!(exponential_search_by(s, |probe| probe.cmp(&seek)), Err(7));
/// let seek = 100;
/// assert_eq!(exponential_search_by(s, |probe| probe.cmp(&seek)), Err(13));
/// let seek = 1;
/// let r = exponential_search_by(s, |probe| probe.cmp(&seek));
/// assert!(match r { Ok(1..=4) => true, _ => false, });
/// ```
#[inline]
pub fn exponential_search_by<T, F>(slice: &[T], mut f: F) -> Result<usize, usize>
where F: FnMut(&T) -> Ordering,
{
let mut index = 1;
while index < slice.len() && f(&slice[index]) == Ordering::Less {
index *= 2;
}
let half_bound = index / 2;
let bound = cmp::min(index + 1, slice.len());
match slice[half_bound..bound].binary_search_by(f) {
Ok(pos) => Ok(half_bound + pos),
Err(pos) => Err(half_bound + pos),
}
}
/// Binary searches this sorted slice with a key extraction function.
///
/// Assumes that the slice is sorted by the key.
///
/// If the value is found then `Ok` is returned, containing the index of the matching element;
/// if the value is not found then `Err` is returned, containing the index where a matching element
/// could be inserted while maintaining sorted order.
///
/// # Examples
///
/// Looks up a series of four elements. The first is found, with a
/// uniquely determined position; the second and third are not
/// found; the fourth could match any position in `[1, 4]`.
///
/// ```
/// use slice_group_by::exponential_search_by_key;
///
/// let s = &[(0, 0), (2, 1), (4, 1), (5, 1), (3, 1),
/// (1, 2), (2, 3), (4, 5), (5, 8), (3, 13),
/// (1, 21), (2, 34), (4, 55)];
///
/// assert_eq!(exponential_search_by_key(s, &13, |&(a,b)| b), Ok(9));
/// assert_eq!(exponential_search_by_key(s, &4, |&(a,b)| b), Err(7));
/// assert_eq!(exponential_search_by_key(s, &100, |&(a,b)| b), Err(13));
/// let r = exponential_search_by_key(s, &1, |&(a,b)| b);
/// assert!(match r { Ok(1..=4) => true, _ => false, });
/// ```
#[inline]
pub fn exponential_search_by_key<T, B, F>(slice: &[T], b: &B, mut f: F) -> Result<usize, usize>
where F: FnMut(&T) -> B,
B: Ord
{
exponential_search_by(slice, |k| f(k).cmp(b))
}
/// A convenient trait to construct an iterator returning non-overlapping groups
/// defined by a predicate.
pub trait GroupBy<T>
{
/// Returns an iterator on slice groups based that will use the given function to generate keys
/// and determine groups based on them. It uses *linear search* to iterate over groups.
fn linear_group_by_key<F, K>(&self, func: F) -> LinearGroupByKey<T, F>
where F: FnMut(&T) -> K,
K: PartialEq;
/// Returns an iterator on slice groups using the *linear search* method.
fn linear_group_by<P>(&self, predicate: P) -> LinearGroupBy<T, P>
where P: FnMut(&T, &T) -> bool;
/// Returns an iterator on slice groups based on the [`PartialEq::eq`] method of `T`,
/// it uses *linear search* to iterate over groups.
///
/// [`PartialEq::eq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#tymethod.eq
fn linear_group(&self) -> LinearGroup<T>
where T: PartialEq;
/// Returns an iterator on slice groups based that will use the given function to generate keys
/// and determine groups based on them. It uses *binary search* to iterate over groups.
///
/// The predicate function should implement an order consistent with
/// the sort order of the slice.
fn binary_group_by_key<F, K>(&self, func: F) -> BinaryGroupByKey<T, F>
where F: FnMut(&T) -> K,
K: PartialEq;
/// Returns an iterator on slice groups using the *binary search* method.
///
/// The predicate function should implement an order consistent with
/// the sort order of the slice.
fn binary_group_by<P>(&self, predicate: P) -> BinaryGroupBy<T, P>
where P: FnMut(&T, &T) -> bool;
/// Returns an iterator on slice groups based on the [`PartialEq::eq`] method of `T`,
/// it uses *binary search* to iterate over groups.
///
/// The predicate function should implement an order consistent with
/// the sort order of the slice.
///
/// [`PartialEq::eq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#tymethod.eq
fn binary_group(&self) -> BinaryGroup<T>
where T: PartialEq;
/// Returns an iterator on slice groups based that will use the given function to generate keys
/// and determine groups based on them. It uses *exponential search* to iterate over groups.
///
/// The predicate function should implement an order consistent with
/// the sort order of the slice.
fn exponential_group_by_key<F, K>(&self, func: F) -> ExponentialGroupByKey<T, F>
where F: Fn(&T) -> K,
K: PartialEq;
/// Returns an iterator on slice groups using the *exponential search* method.
///
/// The predicate function should implement an order consistent with
/// the sort order of the slice.
fn exponential_group_by<P>(&self, predicate: P) -> ExponentialGroupBy<T, P>
where P: FnMut(&T, &T) -> bool;
/// Returns an iterator on slice groups based on the [`PartialEq::eq`] method of `T`,
/// it uses *exponential search* to iterate over groups.
///
/// The predicate function should implement an order consistent with
/// the sort order of the slice.
///
/// [`PartialEq::eq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#tymethod.eq
fn exponential_group(&self) -> ExponentialGroup<T>
where T: PartialEq;
}
/// A convenient trait to construct an iterator returning non-overlapping *mutable*
/// groups defined by a predicate.
pub trait GroupByMut<T>
{
/// Returns an iterator on *mutable* slice groups based that will use the given function
/// to generate keys and determine groups based on them. It uses *linear search*
/// to iterate over groups.
fn linear_group_by_key_mut<F, K>(&mut self, func: F) -> LinearGroupByKeyMut<T, F>
where F: FnMut(&T) -> K,
K: PartialEq;
/// Returns an iterator on *mutable* slice groups using the *linear search* method.
fn linear_group_by_mut<P>(&mut self, predicate: P) -> LinearGroupByMut<T, P>
where P: FnMut(&T, &T) -> bool;
/// Returns an iterator on *mutable* slice groups based on the [`PartialEq::eq`] method of `T`,
/// it uses *linear search* to iterate over groups.
///
/// [`PartialEq::eq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#tymethod.eq
fn linear_group_mut(&mut self) -> LinearGroupMut<T>
where T: PartialEq;
/// Returns an iterator on *mutable* slice groups based that will use the given function
/// to generate keys and determine groups based on them. It uses *binary search*
/// to iterate over groups.
///
/// The predicate function should implement an order consistent with
/// the sort order of the slice.
fn binary_group_by_key_mut<F, K>(&mut self, func: F) -> BinaryGroupByKeyMut<T, F>
where F: FnMut(&T) -> K,
K: PartialEq;
/// Returns an iterator on *mutable* slice groups using the *binary search* method.
///
/// The predicate function should implement an order consistent with
/// the sort order of the slice.
fn binary_group_by_mut<P>(&mut self, predicate: P) -> BinaryGroupByMut<T, P>
where P: FnMut(&T, &T) -> bool;
/// Returns an iterator on *mutable* slice groups based on the [`PartialEq::eq`] method of `T`,
/// it uses *binary search* to iterate over groups.
///
/// The predicate function should implement an order consistent with
/// the sort order of the slice.
///
/// [`PartialEq::eq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#tymethod.eq
fn binary_group_mut(&mut self) -> BinaryGroupMut<T>
where T: PartialEq;
/// Returns an iterator on *mutable* slice groups based that will use the given function
/// to generate keys and determine groups based on them. It uses *exponential search*
/// to iterate over groups.
///
/// The predicate function should implement an order consistent with
/// the sort order of the slice.
fn exponential_group_by_key_mut<F, K>(&mut self, func: F) -> ExponentialGroupByKeyMut<T, F>
where F: Fn(&T) -> K,
K: PartialEq;
/// Returns an iterator on *mutable* slice groups using the *exponential search* method.
///
/// The predicate function should implement an order consistent with
/// the sort order of the slice.
fn exponential_group_by_mut<P>(&mut self, predicate: P) -> ExponentialGroupByMut<T, P>
where P: FnMut(&T, &T) -> bool;
/// Returns an iterator on *mutable* slice groups based on the [`PartialEq::eq`] method of `T`,
/// it uses *exponential search* to iterate over groups.
///
/// The predicate function should implement an order consistent with
/// the sort order of the slice.
///
/// [`PartialEq::eq`]: https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#tymethod.eq
fn exponential_group_mut(&mut self) -> ExponentialGroupMut<T>
where T: PartialEq;
}
impl<T> GroupBy<T> for [T]
{
fn linear_group_by_key<F, K>(&self, func: F) -> LinearGroupByKey<T, F>
where F: FnMut(&T) -> K,
K: PartialEq
{
LinearGroupByKey::new(self, func)
}
fn linear_group_by<P>(&self, predicate: P) -> LinearGroupBy<T, P>
where P: FnMut(&T, &T) -> bool,
{
LinearGroupBy::new(self, predicate)
}
fn linear_group(&self) -> LinearGroup<T>
where T: PartialEq,
{
LinearGroup::new(self)
}
fn binary_group_by_key<F, K>(&self, func: F) -> BinaryGroupByKey<T, F>
where F: FnMut(&T) -> K,
K: PartialEq
{
BinaryGroupByKey::new(self, func)
}
fn binary_group_by<P>(&self, predicate: P) -> BinaryGroupBy<T, P>
where P: FnMut(&T, &T) -> bool,
{
BinaryGroupBy::new(self, predicate)
}
fn binary_group(&self) -> BinaryGroup<T>
where T: PartialEq,
{
BinaryGroup::new(self)
}
fn exponential_group_by_key<F, K>(&self, func: F) -> ExponentialGroupByKey<T, F>
where F: Fn(&T) -> K,
K: PartialEq
{
ExponentialGroupByKey::new(self, func)
}
fn exponential_group_by<P>(&self, predicate: P) -> ExponentialGroupBy<T, P>
where P: FnMut(&T, &T) -> bool,
{
ExponentialGroupBy::new(self, predicate)
}
fn exponential_group(&self) -> ExponentialGroup<T>
where T: PartialEq,
{
ExponentialGroup::new(self)
}
}
impl<T> GroupByMut<T> for [T]
{
fn linear_group_by_key_mut<F, K>(&mut self, func: F) -> LinearGroupByKeyMut<T, F>
where F: FnMut(&T) -> K,
K: PartialEq
{
LinearGroupByKeyMut::new(self, func)
}
fn linear_group_by_mut<P>(&mut self, predicate: P) -> LinearGroupByMut<T, P>
where P: FnMut(&T, &T) -> bool,
{
LinearGroupByMut::new(self, predicate)
}
fn linear_group_mut(&mut self) -> LinearGroupMut<T>
where T: PartialEq,
{
LinearGroupMut::new(self)
}
fn binary_group_by_key_mut<F, K>(&mut self, func: F) -> BinaryGroupByKeyMut<T, F>
where F: FnMut(&T) -> K,
K: PartialEq
{
BinaryGroupByKeyMut::new(self, func)
}
fn binary_group_by_mut<P>(&mut self, predicate: P) -> BinaryGroupByMut<T, P>
where P: FnMut(&T, &T) -> bool,
{
BinaryGroupByMut::new(self, predicate)
}
fn binary_group_mut(&mut self) -> BinaryGroupMut<T>
where T: PartialEq,
{
BinaryGroupMut::new(self)
}
fn exponential_group_by_key_mut<F, K>(&mut self, func: F) -> ExponentialGroupByKeyMut<T, F>
where F: Fn(&T) -> K,
K: PartialEq
{
ExponentialGroupByKeyMut::new(self, func)
}
fn exponential_group_by_mut<P>(&mut self, predicate: P) -> ExponentialGroupByMut<T, P>
where P: FnMut(&T, &T) -> bool,
{
ExponentialGroupByMut::new(self, predicate)
}
fn exponential_group_mut(&mut self) -> ExponentialGroupMut<T>
where T: PartialEq,
{
ExponentialGroupMut::new(self)
}
}
/// A convenient trait to construct an iterator returning non-overlapping `str` slices
/// defined by a predicate.
pub trait StrGroupBy
{
/// Returns an iterator on `str` groups based that will use the given function
/// to generate keys and determine groups based on them. It uses *linear search*
/// to iterate over groups.
fn linear_group_by_key<F, K>(&self, func: F) -> LinearStrGroupByKey<F>
where F: FnMut(char) -> K,
K: PartialEq;
/// Returns an iterator on `str` groups using the *linear search* method.
fn linear_group_by<P>(&self, predicate: P) -> LinearStrGroupBy<P>
where P: FnMut(char, char) -> bool;
/// Returns an iterator on `str` groups based on the [`PartialEq::eq`] method of `char`,
/// it uses *linear search* to iterate over groups.
///
/// [`PartialEq::eq`]: https://doc.rust-lang.org/std/primitive.char.html#impl-PartialEq%3Cchar%3E
fn linear_group(&self) -> LinearStrGroup;
}
/// A convenient trait to construct an iterator returning non-overlapping *mutable* `str` slices
/// defined by a predicate.
pub trait StrGroupByMut
{
/// Returns an iterator on *mutable* `str` groups based that will use the given function
/// to generate keys and determine groups based on them. It uses *linear search*
/// to iterate over groups.
fn linear_group_by_key_mut<F, K>(&mut self, func: F) -> LinearStrGroupByKeyMut<F>
where F: FnMut(char) -> K,
K: PartialEq;
/// Returns an iterator on *mutable* `str` groups using the *linear search* method.
fn linear_group_by_mut<P>(&mut self, predicate: P) -> LinearStrGroupByMut<P>
where P: FnMut(char, char) -> bool;
/// Returns an iterator on *mutable* `str` groups based on the [`PartialEq::eq`] method of `char`,
/// it uses *linear search* to iterate over groups.
///
/// [`PartialEq::eq`]: https://doc.rust-lang.org/std/primitive.char.html#impl-PartialEq%3Cchar%3E
fn linear_group_mut(&mut self) -> LinearStrGroupMut;
}
impl StrGroupBy for str
{
fn linear_group_by_key<F, K>(&self, func: F) -> LinearStrGroupByKey<F>
where F: FnMut(char) -> K,
K: PartialEq
{
LinearStrGroupByKey::new(self, func)
}
fn linear_group_by<P>(&self, predicate: P) -> LinearStrGroupBy<P>
where P: FnMut(char, char) -> bool,
{
LinearStrGroupBy::new(self, predicate)
}
fn linear_group(&self) -> LinearStrGroup {
LinearStrGroup::new(self)
}
}
impl StrGroupByMut for str
{
fn linear_group_by_key_mut<F, K>(&mut self, func: F) -> LinearStrGroupByKeyMut<F>
where F: FnMut(char) -> K,
K: PartialEq
{
LinearStrGroupByKeyMut::new(self, func)
}
fn linear_group_by_mut<P>(&mut self, predicate: P) -> LinearStrGroupByMut<P>
where P: FnMut(char, char) -> bool,
{
LinearStrGroupByMut::new(self, predicate)
}
fn linear_group_mut(&mut self) -> LinearStrGroupMut {
LinearStrGroupMut::new(self)
}
}