Permutation and Choose iterators
This commit is contained in:
commit
b9feba74a4
7 changed files with 325 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
/target
|
||||
/.direnv
|
||||
/.envrc
|
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal 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
8
Cargo.toml
Normal 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
25
examples/four.rs
Normal 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
61
flake.lock
Normal 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
36
flake.nix
Normal 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
185
src/lib.rs
Normal 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))
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue