Skip to content

Added array_permutations (attempt 2) #1014

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
77 changes: 2 additions & 75 deletions src/combinations.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use core::array;
use core::borrow::BorrowMut;
use std::fmt;
use std::iter::FusedIterator;

use super::lazy_buffer::LazyBuffer;
use super::lazy_buffer::{LazyBuffer, PoolIndex};
Copy link
Member

@phimuemue phimuemue Jan 13, 2025

Choose a reason for hiding this comment

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

I think PoolIndex is not really tied to LazyBuffer (it should be compatible with anything you can usize-index into), so it should probably have an own module.

use alloc::vec::Vec;

use crate::adaptors::checked_binomial;
use crate::lazy_buffer::MaybeConstUsize as _;

/// Iterator for `Vec` valued combinations returned by [`.combinations()`](crate::Itertools::combinations)
pub type Combinations<I> = CombinationsGeneric<I, Vec<usize>>;
Expand Down Expand Up @@ -39,79 +39,6 @@ pub struct CombinationsGeneric<I: Iterator, Idx> {
first: bool,
}

pub trait MaybeConstUsize : Clone + Copy + std::fmt::Debug {
/*TODO const*/fn value(self) -> usize;
}

#[derive(Clone, Copy, Debug)]
pub struct ConstUsize<const N: usize>;
impl<const N: usize> MaybeConstUsize for ConstUsize<N> {
fn value(self) -> usize {
N
}
}

impl MaybeConstUsize for usize {
fn value(self) -> usize {
self
}
}

/// A type holding indices of elements in a pool or buffer of items from an inner iterator
/// and used to pick out different combinations in a generic way.
pub trait PoolIndex: BorrowMut<[usize]> {
type Item<T>;
type Length: MaybeConstUsize;

fn extract_item<I: Iterator>(&self, pool: &LazyBuffer<I>) -> Self::Item<I::Item>
where
I::Item: Clone;

fn from_fn<T, F: Fn(usize)->T>(k: Self::Length, f: F) -> Self::Item<T>;

fn len(&self) -> Self::Length;
}

impl PoolIndex for Vec<usize> {
type Item<T> = Vec<T>;
type Length = usize;

fn extract_item<I: Iterator>(&self, pool: &LazyBuffer<I>) -> Self::Item<I::Item>
where
I::Item: Clone
{
pool.get_at(self)
}

fn from_fn<T, F: Fn(usize)->T>(k: Self::Length, f: F) -> Self::Item<T> {
(0..k).map(f).collect()
}

fn len(&self) -> Self::Length {
self.len()
}
}

impl<const K: usize> PoolIndex for [usize; K] {
type Item<T> = [T; K];
type Length = ConstUsize<K>;

fn extract_item<I: Iterator>(&self, pool: &LazyBuffer<I>) -> Self::Item<I::Item>
where
I::Item: Clone
{
pool.get_array(*self)
}

fn from_fn<T, F: Fn(usize)->T>(_k: Self::Length, f: F) -> Self::Item<T> {
std::array::from_fn(f)
}

fn len(&self) -> Self::Length {
ConstUsize::<K>
}
}

impl<I, Idx> Clone for CombinationsGeneric<I, Idx>
where
I: Iterator + Clone,
Expand Down
75 changes: 75 additions & 0 deletions src/lazy_buffer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use alloc::vec::Vec;
use core::borrow::BorrowMut;
use std::iter::Fuse;
use std::ops::Index;

Expand Down Expand Up @@ -77,3 +78,77 @@ where
self.buffer.index(index)
}
}

pub trait MaybeConstUsize: Clone + Copy + std::fmt::Debug {
/*TODO const*/
fn value(self) -> usize;
}

#[derive(Clone, Copy, Debug)]
pub struct ConstUsize<const N: usize>;
impl<const N: usize> MaybeConstUsize for ConstUsize<N> {
fn value(self) -> usize {
N
}
}

impl MaybeConstUsize for usize {
fn value(self) -> usize {
self
}
}

/// A type holding indices of elements in a pool or buffer of items from an inner iterator
/// and used to pick out different combinations in a generic way.
pub trait PoolIndex: BorrowMut<[usize]> {
type Item<T>;
type Length: MaybeConstUsize;

fn extract_item<I: Iterator>(&self, pool: &LazyBuffer<I>) -> Self::Item<I::Item>
where
I::Item: Clone;

fn from_fn<T, F: Fn(usize) -> T>(k: Self::Length, f: F) -> Self::Item<T>;

fn len(&self) -> Self::Length;
}

impl PoolIndex for Vec<usize> {
type Item<T> = Vec<T>;
type Length = usize;

fn extract_item<I: Iterator>(&self, pool: &LazyBuffer<I>) -> Self::Item<I::Item>
where
I::Item: Clone,
{
pool.get_at(self)
}

fn from_fn<T, F: Fn(usize) -> T>(k: Self::Length, f: F) -> Self::Item<T> {
(0..k).map(f).collect()
}

fn len(&self) -> Self::Length {
self.len()
}
}

impl<const K: usize> PoolIndex for [usize; K] {
type Item<T> = [T; K];
type Length = ConstUsize<K>;

fn extract_item<I: Iterator>(&self, pool: &LazyBuffer<I>) -> Self::Item<I::Item>
where
I::Item: Clone,
{
pool.get_array(*self)
}

fn from_fn<T, F: Fn(usize) -> T>(_k: Self::Length, f: F) -> Self::Item<T> {
std::array::from_fn(f)
}

fn len(&self) -> Self::Length {
ConstUsize::<K>
}
}
24 changes: 13 additions & 11 deletions src/permutations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ use alloc::vec::Vec;
use std::fmt;
use std::iter::FusedIterator;

use super::lazy_buffer::LazyBuffer;
use super::lazy_buffer::{LazyBuffer, MaybeConstUsize as _, PoolIndex};
use crate::size_hint::{self, SizeHint};
use crate::combinations::{MaybeConstUsize, PoolIndex};

#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
pub struct PermutationsGeneric<I: Iterator, Idx: PoolIndex> {
Expand Down Expand Up @@ -39,7 +38,7 @@ enum PermutationState<Idx: PoolIndex> {
Loaded {
indices: Box<[usize]>,
cycles: Box<[usize]>, // TODO Should be Idx::Item<usize>
k: Idx::Length, // TODO Should be inferred from cycles
k: Idx::Length, // TODO Should be inferred from cycles
},
/// No permutation left to generate.
End,
Expand Down Expand Up @@ -80,19 +79,18 @@ where
*state = PermutationState::End;
return None;
}
*state = PermutationState::Buffered { k, min_n: k.value() };
*state = PermutationState::Buffered {
k,
min_n: k.value(),
};
}
Some(Idx::from_fn(k, |i| vals[i].clone()))
}
PermutationState::Buffered { k, min_n } => {
if vals.get_next() {
// TODO This is ugly. Maybe working on indices is better?
let item = Idx::from_fn(*k, |i| {
vals[if i==k.value()-1 {
*min_n
} else {
i
}].clone()
vals[if i == k.value() - 1 { *min_n } else { i }].clone()
});
*min_n += 1;
Some(item)
Expand All @@ -109,11 +107,15 @@ where
}
}
let item = Idx::from_fn(*k, |i| vals[indices[i]].clone());
*state = PermutationState::Loaded { indices, cycles, k:*k };
*state = PermutationState::Loaded {
indices,
cycles,
k: *k,
};
Some(item)
}
}
PermutationState::Loaded { indices, cycles, k} => {
PermutationState::Loaded { indices, cycles, k } => {
if advance(indices, cycles) {
*state = PermutationState::End;
return None;
Expand Down