Permutation and Choose iterators

This commit is contained in:
asonix 2024-05-26 13:38:08 -05:00
commit b9feba74a4
7 changed files with 325 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
/target
/.direnv
/.envrc

7
Cargo.lock generated Normal file
View file

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "permute"
version = "0.1.0"

8
Cargo.toml Normal file
View file

@ -0,0 +1,8 @@
[package]
name = "permute"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

25
examples/four.rs Normal file
View file

@ -0,0 +1,25 @@
fn main() {
let permute_input = [1, 2, 3, 4];
for (i, permutation) in permute::permute(permute_input.clone()).enumerate() {
println!("{i}: {permutation:?}");
}
let (size, _) = permute::permute(permute_input).size_hint();
println!("size {size}");
println!();
let choose_input = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (i, chosen) in permute::choose::<_, 4>(&choose_input).enumerate() {
println!("{i}: {chosen:?}");
}
let (size, _) = permute::choose::<_, 4>(&choose_input).size_hint();
println!("size {size}");
println!();
for (i, permutation) in permute::choose_permute::<_, 4>(&choose_input).enumerate() {
println!("{i}: {permutation:?}");
}
let (size, _) = permute::choose_permute::<_, 4>(&choose_input).size_hint();
println!("size {size}");
}

61
flake.lock Normal file
View file

@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1716509168,
"narHash": "sha256-4zSIhSRRIoEBwjbPm3YiGtbd8HDWzFxJjw5DYSDy1n8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "bfb7a882678e518398ce9a31a881538679f6f092",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

36
flake.nix Normal file
View file

@ -0,0 +1,36 @@
{
description = "actix-http";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
};
in
{
packages.default = pkgs.hello;
devShell = with pkgs; mkShell {
nativeBuildInputs = [
cargo
cargo-outdated
clippy
openssl
pkg-config
rust-analyzer
rustc
rustfmt
stdenv.cc
taplo
];
RUST_SRC_PATH = "${pkgs.rust.packages.stable.rustPlatform.rustLibSrc}";
};
});
}

185
src/lib.rs Normal file
View file

@ -0,0 +1,185 @@
pub fn choose_permute<T: Clone, const SIZE: usize>(
source: &[T],
) -> impl Iterator<Item = [T; SIZE]> + '_ {
ChoosePermute {
choose: choose(source),
permute: None,
}
}
pub fn permute<T: Clone, const SIZE: usize>(source: [T; SIZE]) -> Permute<T, SIZE> {
Permute {
source,
count: [0; SIZE],
closed: false,
}
}
pub fn choose<T: Clone, const SIZE: usize>(source: &[T]) -> Choose<'_, T, SIZE> {
if source.len() < SIZE {
panic!("Source to small to choose {SIZE} elements");
}
Choose {
source,
count: [0; SIZE],
closed: false,
}
}
pub struct ChoosePermute<'a, T, const SIZE: usize> {
choose: Choose<'a, T, SIZE>,
permute: Option<Permute<T, SIZE>>,
}
impl<'a, T: Clone, const SIZE: usize> Iterator for ChoosePermute<'a, T, SIZE> {
type Item = [T; SIZE];
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(permute) = self.permute.as_mut() {
if let Some(item) = permute.next() {
return Some(item);
}
self.permute.take();
}
let chosen = self.choose.next()?;
self.permute = Some(permute(chosen));
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let size = self.choose.size_hint().0 * factorial(SIZE);
(size, Some(size))
}
}
pub struct Choose<'a, T, const SIZE: usize> {
source: &'a [T],
count: [usize; SIZE],
closed: bool,
}
pub struct Permute<T, const SIZE: usize> {
source: [T; SIZE],
count: [usize; SIZE],
closed: bool,
}
impl<'a, T: Clone, const SIZE: usize> Choose<'a, T, SIZE> {
fn increment(&mut self) {
let mut any_updated = false;
for i in (0..SIZE).rev() {
let total_offset = self.count.iter().copied().sum::<usize>() + SIZE;
if total_offset < self.source.len() {
self.count[i] += 1;
any_updated = true;
break;
} else {
self.count[i] = 0;
}
}
self.closed = !any_updated;
}
fn choose(&self) -> [T; SIZE] {
let mut idx = 0;
let mut total_offset = 0;
self.count.clone().map(|offset| {
total_offset += offset;
let i = idx + total_offset;
idx += 1;
self.source[i].clone()
})
}
}
impl<'a, T: Clone, const SIZE: usize> Iterator for Choose<'a, T, SIZE> {
type Item = [T; SIZE];
fn next(&mut self) -> Option<Self::Item> {
if self.closed {
None
} else {
self.increment();
Some(self.choose())
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let numerator = factorial(self.source.len());
let denominator = factorial(SIZE) * factorial(self.source.len() - SIZE);
let size = numerator / denominator;
(size, Some(size))
}
}
impl<T: Clone, const SIZE: usize> Permute<T, SIZE> {
fn increment(&mut self) {
let mut any_updated = false;
for (idx, count) in self.count.iter_mut().rev().enumerate() {
if *count < idx {
*count += 1;
any_updated = true;
break;
} else {
*count = 0;
}
}
self.closed = !any_updated;
}
fn permmute(&self) -> [T; SIZE] {
let mut out = self.source.clone();
for (idx, count) in self.count.iter().enumerate() {
if *count > 0 {
out.swap(idx, idx + *count);
}
}
out
}
}
fn factorial(num: usize) -> usize {
if num == 0 {
return 0;
}
(1..=num).fold(1, |acc, item| acc * item)
}
impl<T: Clone, const SIZE: usize> Iterator for Permute<T, SIZE> {
type Item = [T; SIZE];
fn next(&mut self) -> Option<Self::Item> {
if self.closed {
None
} else {
self.increment();
Some(self.permmute())
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let size = factorial(SIZE);
(size, Some(size))
}
}