Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
target
Cargo.lock
/src/btree_multiset.rs
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "multiset"
version = "0.0.5"
version = "0.1.0"
Comment thread
Ten0 marked this conversation as resolved.
Outdated
repository = "https://github.com/jmitchell/multiset"
description = "Multisets/bags"
keywords = ["multiset","bag","data-structure","collection","count"]
Expand Down
14 changes: 14 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
fn main() {
Comment thread
Ten0 marked this conversation as resolved.
let btree_multiset_code = std::fs::read_to_string("./src/hash_multiset.rs")
.expect("Could not open hash_multiset source file")
.replace("Hash + Eq", "Ord")
.replace("Eq + Hash", "Ord")
.replace("hash_map::", "btree_map::")
.replace("HashMap", "BTreeMap")
.replace("HashMultiSet", "BTreeMultiSet")
.replace("use std::hash::Hash;\n", "")
.replace("hash-based multiset", "tree-based multiset");
std::fs::write("./src/btree_multiset.rs", btree_multiset_code.as_bytes())
.expect("Could not write btree_multiset file");
println!("cargo:rerun-if-changed=./src/hash_multiset.rs");
}
58 changes: 6 additions & 52 deletions src/multiset.rs → src/hash_multiset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
// except according to those terms.
#![warn(missing_docs)]

use super::Iter;

use std::borrow::Borrow;
use std::collections::hash_map;
use std::collections::hash_map::{Entry, Keys};
use std::collections::hash_map::{self, Entry, Keys};
use std::collections::HashMap;
use std::fmt;
use std::hash::Hash;
Expand All @@ -23,45 +24,6 @@ pub struct HashMultiSet<K> {
size: usize,
}

/// An iterator over the items of a `HashMultiSet`.
///
/// This `struct` is created by the [`iter`] method on [`HashMultiSet`].
pub struct Iter<'a, K: 'a> {
iter: hash_map::Iter<'a, K, usize>,
duplicate: Option<(&'a K, &'a usize)>,
duplicate_index: usize,
}

impl<'a, K> Clone for Iter<'a, K> {
fn clone(&self) -> Iter<'a, K> {
Iter {
iter: self.iter.clone(),
duplicate: self.duplicate.clone(),
duplicate_index: self.duplicate_index,
}
}
}
impl<'a, K> Iterator for Iter<'a, K> {
type Item = &'a K;

fn next(&mut self) -> Option<&'a K> {
if self.duplicate.is_none() {
self.duplicate = self.iter.next();
}
if self.duplicate.is_some() {
let (key, count) = self.duplicate.unwrap();
self.duplicate_index += 1;
if &self.duplicate_index >= count {
self.duplicate = None;
self.duplicate_index = 0;
}
Some(key)
} else {
None
}
}
}

impl<K> HashMultiSet<K>
where
K: Eq + Hash,
Expand Down Expand Up @@ -100,12 +62,8 @@ where
/// }
/// assert_eq!(3, multiset.iter().count());
/// ```
pub fn iter(&self) -> Iter<K> {
Iter {
iter: self.elem_counts.iter(),
duplicate: None,
duplicate_index: 0,
}
pub fn iter(&self) -> Iter<&K, &usize, hash_map::Iter<K, usize>> {
Iter::new(self.elem_counts.iter(), self.size)
}

/// Returns true if the multiset contains no elements.
Expand All @@ -126,10 +84,6 @@ where

/// Returns `true` if the multiset contains a value.
///
/// The value may be any borrowed form of the set's value type, but
/// [`Hash`] and [`Eq`] on the borrowed form *must* match those for
/// the value type.
///
/// # Examples
///
/// ```
Expand All @@ -142,7 +96,7 @@ where
pub fn contains<Q: ?Sized>(&self, value: &Q) -> bool
where
K: Borrow<Q>,
Q: Hash + Eq,
Q: Eq + Hash,
{
self.elem_counts.contains_key(value)
}
Expand Down
191 changes: 191 additions & 0 deletions src/iter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// Copyright 2019 multiset developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![warn(missing_docs)]

use std::borrow::Borrow;
use std::marker::PhantomData;

/// An iterator over the items of a `MultiSet`.
///
/// This `struct` is created by the [`iter`](super::HashMultiSet::iter) method on
/// [`HashMultiSet`](super::HashMultiSet) or [`BTreeMultiSet`](super::BTreeMultiSet).
pub struct Iter<K: Clone, V: Borrow<usize>, InnerIter: Iterator<Item = (K, V)>> {
pub(crate) iter: InnerIter,
pub(crate) duplicate: Option<<InnerIter as Iterator>::Item>,
pub(crate) duplicate_index: usize,
pub(crate) duplicate_back: Option<<InnerIter as Iterator>::Item>,
pub(crate) duplicate_index_back: usize,
pub(crate) len: usize,
pub(crate) _ghost: PhantomData<*const (K, V)>,
}

impl<K: Clone, V: Borrow<usize>, InnerIter: Iterator<Item = (K, V)> + ExactSizeIterator>
Iter<K, V, InnerIter>
{
pub(crate) fn new(iter: InnerIter, len: usize) -> Self {
Iter {
iter,
duplicate: None,
duplicate_index: 0,
duplicate_back: None,
duplicate_index_back: 0,
len,
_ghost: PhantomData,
}
}
}

impl<K: Clone, V: Borrow<usize>, InnerIter: Iterator<Item = (K, V)> + Clone> Clone
for Iter<K, V, InnerIter>
where
<InnerIter as Iterator>::Item: Clone,
{
fn clone(&self) -> Iter<K, V, InnerIter> {
Comment thread
Ten0 marked this conversation as resolved.
Outdated
Iter {
iter: self.iter.clone(),
duplicate: self.duplicate.clone(),
duplicate_index: self.duplicate_index,
duplicate_back: self.duplicate_back.clone(),
duplicate_index_back: self.duplicate_index_back,
len: self.len,
_ghost: PhantomData,
}
}
}

impl<K: Clone, V: Borrow<usize>, InnerIter: Iterator<Item = (K, V)>> Iterator
for Iter<K, V, InnerIter>
{
type Item = K;

fn next(&mut self) -> Option<Self::Item> {
if self.duplicate.is_none() {
self.duplicate = self.iter.next();
}
if let Some((key, count)) = self.duplicate.as_ref() {
self.duplicate_index += 1;
let key = key.clone();
if self.duplicate_index >= *count.borrow() {
self.duplicate = None;
self.duplicate_index = 0;
}
self.len -= 1;
Some(key)
} else {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clippy: this else { if .. } block can be collapsed to else if.

if let Some((key, count)) = self.duplicate_back.as_ref() {
self.duplicate_index_back += 1;
let key = key.clone();
if self.duplicate_index_back >= *count.borrow() {
self.duplicate_back = None;
}
self.len -= 1;
Some(key)
} else {
None
}
}
}

fn count(self) -> usize {
self.len()
}

fn fold<B, F>(self, init: B, mut f: F) -> B
Comment thread
Ten0 marked this conversation as resolved.
where
F: FnMut(B, Self::Item) -> B,
{
let duplicate_index = self.duplicate_index;
let duplicate_index_back = self.duplicate_index_back;
self.duplicate
.map(move |(val, count)| (val, *count.borrow() - duplicate_index))
.into_iter()
.chain(self.iter.map(move |(val, count)| (val, *count.borrow())))
.chain(
self.duplicate_back
.map(move |(val, count)| (val, *count.borrow() - duplicate_index_back))
.into_iter(),
)
.fold(init, move |acc, (val, count)| {
(0..count).fold(acc, |acc, _| f(acc, val.clone()))
})
}

fn size_hint(&self) -> (usize, Option<usize>) {
let l = self.len();
(l, Some(l))
}
}

impl<K: Clone, V: Borrow<usize>, InnerIter: Iterator<Item = (K, V)>> ExactSizeIterator
for Iter<K, V, InnerIter>
{
fn len(&self) -> usize {
self.len
}
}

impl<K: Clone, V: Borrow<usize>, InnerIter: Iterator<Item = (K, V)> + DoubleEndedIterator>
DoubleEndedIterator for Iter<K, V, InnerIter>
{
fn next_back(&mut self) -> Option<Self::Item> {
if self.duplicate_back.is_none() {
self.duplicate_back = self.iter.next_back();
}
if let Some((key, count)) = self.duplicate_back.as_ref() {
self.duplicate_index_back += 1;
let key = key.clone();
if self.duplicate_index_back >= *count.borrow() {
self.duplicate_back = None;
self.duplicate_index_back = 0;
}
self.len -= 1;
Some(key)
} else {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clippy: this else { if .. } block can be collapsed to else if.

if let Some((key, count)) = self.duplicate.as_ref() {
self.duplicate_index += 1;
let key = key.clone();
if self.duplicate_index >= *count.borrow() {
self.duplicate = None;
}
self.len -= 1;
Some(key)
} else {
None
}
}
}

fn rfold<B, F>(self, init: B, mut f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
let duplicate_index = self.duplicate_index;
let duplicate_index_back = self.duplicate_index_back;
self.duplicate_back
.map(move |(val, count)| (val, *count.borrow() - duplicate_index_back))
.into_iter()
.chain(
self.iter
.rev()
.map(move |(val, count)| (val, *count.borrow())),
)
.chain(
self.duplicate
.map(move |(val, count)| (val, *count.borrow() - duplicate_index))
.into_iter(),
)
.fold(init, move |acc, (val, count)| {
(0..count).fold(acc, |acc, _| f(acc, val.clone()))
})
}
}

impl<K: Clone, V: Borrow<usize>, InnerIter: Iterator<Item = (K, V)> + std::iter::FusedIterator>
std::iter::FusedIterator for Iter<K, V, InnerIter>
{
}
8 changes: 6 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
//! permit duplicates. Consequently, they're useful for maintaining a
//! count of distinct values.

mod multiset;
mod btree_multiset;
mod hash_multiset;
mod iter;

pub use multiset::{HashMultiSet, Iter};
pub use btree_multiset::BTreeMultiSet;
pub use hash_multiset::HashMultiSet;
pub use iter::Iter;