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