use log::info;
pub struct Combinations {
item_count: usize,
max_len: usize,
items: Vec<usize>,
current_ix: usize,
}
impl Combinations {
pub fn new(n: usize, k_max: usize) -> Self {
Combinations {
item_count: n,
max_len: k_max,
items: Vec::with_capacity(k_max),
current_ix: 0,
}
}
}
impl Iterator for Combinations {
type Item = Vec<usize>;
fn next(&mut self) -> Option<Self::Item> {
if self.current_ix >= self.item_count {
if let Some(prev_ix) = self.items.pop() {
self.current_ix = prev_ix + 1;
self.next()
} else {
None
}
} else {
self.items.push(self.current_ix);
let item = Some(self.items.clone());
if self.items.len() < self.max_len {
self.current_ix = self.items.iter().max().unwrap() + 1;
} else {
self.items.pop();
self.current_ix += 1;
}
item
}
}
}
pub struct CombinationsFilter<F: Fn(usize, usize) -> bool> {
item_count: usize,
max_len: usize,
items: Vec<usize>,
current_ix: usize,
item_filter: F,
filter_calls: usize,
}
impl<F: Fn(usize, usize) -> bool> CombinationsFilter<F> {
pub fn new(n: usize, k_max: usize, ignore: F) -> Self {
CombinationsFilter {
item_count: n,
max_len: k_max,
items: Vec::with_capacity(k_max),
current_ix: 0,
item_filter: ignore,
filter_calls: 0,
}
}
}
pub mod par {
pub struct CombFilt<F: Fn(usize, usize) -> bool + Send + Sync> {
item_count: usize,
max_len: usize,
current_ix: usize,
ix0_max: usize,
item_filter: F,
}
impl<F: Fn(usize, usize) -> bool + Send + Sync>
rayon::iter::IntoParallelIterator for super::CombinationsFilter<F>
{
type Iter = CombFilt<F>;
type Item = Vec<usize>;
fn into_par_iter(self) -> Self::Iter {
if self.current_ix != 0 {
panic!("CombinationsFilter.current_ix != 0")
} else if !self.items.is_empty() {
panic!("CombinationsFilter.items is not empty")
}
CombFilt {
item_count: self.item_count,
max_len: self.max_len,
current_ix: self.current_ix,
item_filter: self.item_filter,
ix0_max: self.item_count,
}
}
}
pub struct CombFiltProducer<'a, F: Fn(usize, usize) -> bool + Send + Sync> {
item_count: usize,
max_len: usize,
items: Vec<usize>,
current_ix: usize,
ix0_max: usize,
item_filter: &'a F,
}
impl<F: Fn(usize, usize) -> bool + Send + Sync>
rayon::iter::ParallelIterator for CombFilt<F>
{
type Item = Vec<usize>;
fn drive_unindexed<C>(self, consumer: C) -> C::Result
where
C: rayon::iter::plumbing::UnindexedConsumer<Self::Item>,
{
let producer = CombFiltProducer {
item_count: self.item_count,
max_len: self.max_len,
items: vec![],
current_ix: self.current_ix,
ix0_max: self.ix0_max,
item_filter: &self.item_filter,
};
rayon::iter::plumbing::bridge_unindexed(producer, consumer)
}
}
impl<'a, F: Fn(usize, usize) -> bool + Send + Sync> CombFiltProducer<'a, F> {
pub fn split_at(&self) -> Option<usize> {
let too_narrow = self.ix0_max - self.current_ix <= 1;
let not_empty = !self.items.is_empty();
if too_narrow || not_empty {
None
} else {
let split_at =
self.current_ix + (self.ix0_max - self.current_ix) / 2;
Some(split_at)
}
}
}
impl<'a, F: Fn(usize, usize) -> bool + Send + Sync> Iterator
for CombFiltProducer<'a, F>
{
type Item = Vec<usize>;
fn next(&mut self) -> Option<Self::Item> {
let ix_ub = if self.items.is_empty() {
self.ix0_max
} else {
self.item_count
};
while self.current_ix < ix_ub {
if self
.items
.iter()
.any(|x| (self.item_filter)(*x, self.current_ix))
{
self.current_ix += 1;
continue;
}
self.items.push(self.current_ix);
let item = Some(self.items.clone());
if self.items.len() < self.max_len {
self.current_ix = self.items.iter().max().unwrap() + 1;
} else {
self.items.pop();
self.current_ix += 1;
}
return item;
}
if let Some(prev_ix) = self.items.pop() {
self.current_ix = prev_ix + 1;
self.next()
} else {
None
}
}
}
impl<'a, F: Fn(usize, usize) -> bool + Send + Sync>
rayon::iter::plumbing::UnindexedProducer for CombFiltProducer<'a, F>
{
type Item = Vec<usize>;
fn split(self) -> (Self, Option<Self>) {
if let Some(split_at) = self.split_at() {
let low = Self {
item_count: self.item_count,
max_len: self.max_len,
items: vec![],
current_ix: self.current_ix,
item_filter: self.item_filter,
ix0_max: split_at,
};
let high = Self {
item_count: self.item_count,
max_len: self.max_len,
items: vec![],
current_ix: split_at,
item_filter: self.item_filter,
ix0_max: self.ix0_max,
};
(low, Some(high))
} else {
(self, None)
}
}
fn fold_with<G>(mut self, folder: G) -> G
where
G: rayon::iter::plumbing::Folder<Self::Item>,
{
let mut folder = folder;
for val in &mut self {
folder = folder.consume(val);
}
folder
}
}
#[cfg(test)]
mod tests {
use super::super::CombinationsFilter;
use super::CombFilt;
use log::info;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
fn init() {
let _ = env_logger::Builder::from_env(
env_logger::Env::default().default_filter_or("info"),
)
.is_test(true)
.try_init();
}
#[test]
fn test_par_comb_filter_1() {
init();
let comb = CombinationsFilter::new(5, 3, |_x, _y| false);
let combs: Vec<_> = comb.collect();
let expected_count = 5 + 10 + 10;
assert_eq!(expected_count, combs.len());
info!("CombinationsFilter returned:");
for c in &combs {
info!(" {:?}", c);
}
info!("");
let pcomb: CombFilt<_> =
CombinationsFilter::new(5, 3, |_x, _y| false).into_par_iter();
let pcombs: Vec<_> = pcomb.collect();
info!("Parallel returned:");
for c in &pcombs {
info!(" {:?}", c);
}
info!("");
assert_eq!(expected_count, pcombs.len());
assert_eq!(combs, pcombs)
}
#[test]
fn test_par_comb_filter_2() {
init();
let comb = CombinationsFilter::new(5, 3, |x, y| x == 2 || y == 2);
let combs: Vec<_> = comb.collect();
let expected_count = 15;
assert_eq!(expected_count, combs.len());
info!("CombinationsFilter returned:");
for c in &combs {
info!(" {:?}", c);
}
info!("");
let pcomb: CombFilt<_> =
CombinationsFilter::new(5, 3, |x, y| x == 2 || y == 2)
.into_par_iter();
let pcombs: Vec<_> = pcomb.collect();
info!("Parallel returned:");
for c in &pcombs {
info!(" {:?}", c);
}
info!("");
assert_eq!(expected_count, pcombs.len());
assert_eq!(combs, pcombs)
}
}
}
impl<F: Fn(usize, usize) -> bool> Iterator for CombinationsFilter<F> {
type Item = Vec<usize>;
fn next(&mut self) -> Option<Self::Item> {
while self.current_ix < self.item_count {
self.filter_calls += 1;
if self
.items
.iter()
.any(|x| (self.item_filter)(*x, self.current_ix))
{
self.current_ix += 1;
continue;
}
self.items.push(self.current_ix);
let item = Some(self.items.clone());
if self.items.len() < self.max_len {
self.current_ix = self.items.iter().max().unwrap() + 1;
} else {
self.items.pop();
self.current_ix += 1;
}
return item;
}
if let Some(prev_ix) = self.items.pop() {
self.current_ix = prev_ix + 1;
self.next()
} else {
info!("Made {} calls to filter fn", self.filter_calls);
None
}
}
}
pub struct KCombinations {
item_count: usize,
want_len: usize,
items: Vec<usize>,
current_ix: usize,
}
impl KCombinations {
pub fn new(n: usize, k: usize) -> Self {
KCombinations {
item_count: n,
want_len: k,
items: Vec::with_capacity(k),
current_ix: 0,
}
}
}
impl Iterator for KCombinations {
type Item = Vec<usize>;
fn next(&mut self) -> Option<Self::Item> {
if self.current_ix >= self.item_count {
if let Some(prev_ix) = self.items.pop() {
self.current_ix = prev_ix + 1;
self.next()
} else {
None
}
} else {
self.items.push(self.current_ix);
if self.items.len() < self.want_len {
self.current_ix = self.items.iter().max().unwrap() + 1;
self.next()
} else {
let item = Some(self.items.clone());
self.items.pop();
self.current_ix += 1;
item
}
}
}
}
#[cfg(test)]
mod tests {
use super::{Combinations, CombinationsFilter, KCombinations};
use log::info;
fn init() {
let _ = env_logger::Builder::from_env(
env_logger::Env::default().default_filter_or("info"),
)
.is_test(true)
.try_init();
}
#[test]
fn test_combinations_1() {
init();
let comb = Combinations::new(5, 3);
let combs: Vec<_> = comb.collect();
let expected_count = 5 + 10 + 10;
assert_eq!(expected_count, combs.len());
for c in &combs {
info!("{:?}", c);
}
}
#[test]
fn test_combinations_filter_1() {
init();
let filter = Box::new(|i, j| j == (2 * i));
let comb = CombinationsFilter::new(5, 3, filter);
let combs: Vec<_> = comb.collect();
let expected_count = 5 + 10 + 10 - 7;
assert_eq!(expected_count, combs.len());
for c in &combs {
info!("{:?}", c);
}
}
#[test]
fn test_kcombinations_1() {
init();
let ks: [usize; 3] = [1, 2, 3];
for k in &ks {
let comb = KCombinations::new(5, *k);
let combs: Vec<_> = comb.collect();
if *k == 1 {
assert_eq!(5, combs.len())
} else if *k == 2 || *k == 3 {
assert_eq!(10, combs.len())
} else {
unreachable!("k should be in [1, 2, 2]")
}
for comb in &combs {
assert_eq!(comb.len(), *k)
}
}
}
}