From 3336f8445a5baad1c20bcbf980880a5640262396 Mon Sep 17 00:00:00 2001 From: U2A5F Date: Mon, 18 Nov 2024 22:02:58 +0800 Subject: [PATCH] permutations --- README.md | 8 ++++ code_gen/Cargo.toml | 3 +- code_gen/src/code_gen.rs | 88 ++++++++++++++++++++++++++++++++++ tuples/Cargo.toml | 2 + tuples/src/gen/permutations.rs | 50 +++++++++++++++++++ tuples/src/lib.rs | 7 ++- tuples/src/permutations.rs | 21 ++++++++ 7 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 tuples/src/gen/permutations.rs create mode 100644 tuples/src/permutations.rs diff --git a/README.md b/README.md index 1260fcf..a949064 100644 --- a/README.md +++ b/README.md @@ -373,3 +373,11 @@ Provides many useful tools related to tuples let a = a.sorted(); assert_eq!(a, (0, 2, 5, 6, 6, 8)) ``` + +- permutations + + ```rust + let a = (1, '2', "3"); + let r = a.permutations_2(); + assert_eq!(r, ((1, '2'), (1, "3"), ('2', 1), ('2', "3"), ("3", 1), ("3", '2'))); + ``` diff --git a/code_gen/Cargo.toml b/code_gen/Cargo.toml index 121bcd4..ecf34c5 100644 --- a/code_gen/Cargo.toml +++ b/code_gen/Cargo.toml @@ -9,4 +9,5 @@ edition = "2021" [dependencies] syn = "2.0" quote = "1.0" -proc-macro2 = "1.0" \ No newline at end of file +proc-macro2 = "1.0" +itertools = "0.13" diff --git a/code_gen/src/code_gen.rs b/code_gen/src/code_gen.rs index 17719ef..e143153 100644 --- a/code_gen/src/code_gen.rs +++ b/code_gen/src/code_gen.rs @@ -1,3 +1,4 @@ +use itertools::Itertools; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote}; use std::{fs, path::Path}; @@ -33,6 +34,7 @@ pub fn code_gen(out_dir: &Path) { gen_capt(&ctx, &out_dir); gen_tuple_get(&ctx, &out_dir); gen_tuple_swap(&ctx, &out_dir); + gen_permutations(&ctx, &out_dir); } #[allow(dead_code)] @@ -1482,3 +1484,89 @@ fn gen_tuple_swap_cart(ctx: &Ctx) -> TokenStream { }; tks } + +fn gen_permutations(ctx: &Ctx, out_dir: &Path) { + let items = (2..4usize).into_iter().map(|i| gen_permutations_size(ctx, i)); + let tks = quote! { #(#items)* }; + let mut code = tks.to_string(); + code.insert_str(0, "// This file is by code gen, do not modify\n\n"); + let dest_path = Path::new(out_dir).join("permutations.rs"); + fs::write(&dest_path, code).unwrap(); +} + +fn gen_permutations_size(ctx: &Ctx, size: usize) -> TokenStream { + let trait_name = format_ident!("TuplePermutations{}", size); + let fn_name = format_ident!("permutations_{}", size); + + let impls = (size..33usize).into_iter().filter(|i| i.pow(size as u32) - i < 33).map(|i| gen_permutations_impl_n(ctx, size, i, &trait_name, &fn_name)); + + let doc = format!("Permutation by {size}"); + + let tks = quote! { + #[doc = #doc] + pub trait #trait_name { + type Output; + + #[doc = #doc] + fn #fn_name(self) -> Self::Output; + } + + #(#impls)* + }; + tks +} + +fn gen_permutations_impl_n(ctx: &Ctx, size: usize, n: usize, trait_name: &Ident, fn_name: &Ident) -> TokenStream { + let nts = &ctx.nts[0..n]; + let size_lits = &ctx.size_lits[0..n]; + + let output_types = nts + .iter() + .permutations(size) + .map(|v| { + quote! { + (#(#v,)*) + } + }) + .collect::>(); + + let mut set = std::collections::HashSet::::new(); + let output_impls = size_lits + .iter() + .enumerate() + .permutations(size) + .collect::>() + .into_iter() + .rev() + .map(|v| { + let v = v.into_iter().map(|(i, l)| { + if set.contains(&i) { + quote! { + self.#l.clone() + } + } else { + set.insert(i); + quote! { + self.#l + } + } + }); + quote! { + (#(#v,)*) + } + }) + .collect::>() + .into_iter() + .rev(); + + let tks = quote! { + impl<#(#nts:Clone),*> #trait_name for (#(#nts,)*) { + type Output = (#(#output_types,)*); + + fn #fn_name(self) -> Self::Output { + (#(#output_impls,)*) + } + } + }; + tks +} diff --git a/tuples/Cargo.toml b/tuples/Cargo.toml index 93b5b5e..81f21c4 100644 --- a/tuples/Cargo.toml +++ b/tuples/Cargo.toml @@ -37,6 +37,7 @@ default = [ "capt", "tuple_get", "sort", + "permutations" ] flatten = [] re-exports = [] @@ -56,6 +57,7 @@ tuple_iter = [] tuple_map = [] tuple_meta = [] tuple_swap_n = [] +permutations = [] [package.metadata.docs.rs] all-features = true diff --git a/tuples/src/gen/permutations.rs b/tuples/src/gen/permutations.rs new file mode 100644 index 0000000..c14cb87 --- /dev/null +++ b/tuples/src/gen/permutations.rs @@ -0,0 +1,50 @@ +// This file is by code gen, do not modify + +#[doc = "Permutation by 2"] +pub trait TuplePermutations2 { + type Output; + #[doc = "Permutation by 2"] + fn permutations_2(self) -> Self::Output; +} +impl TuplePermutations2 for (T0, T1) { + type Output = ((T0, T1), (T1, T0)); + fn permutations_2(self) -> Self::Output { + ((self.0.clone(), self.1.clone()), (self.1, self.0)) + } +} +impl TuplePermutations2 for (T0, T1, T2) { + type Output = ((T0, T1), (T0, T2), (T1, T0), (T1, T2), (T2, T0), (T2, T1)); + fn permutations_2(self) -> Self::Output { + ((self.0.clone(), self.1.clone()), (self.0.clone(), self.2.clone()), (self.1.clone(), self.0.clone()), (self.1.clone(), self.2.clone()), (self.2.clone(), self.0), (self.2, self.1)) + } +} +impl TuplePermutations2 for (T0, T1, T2, T3) { + type Output = ((T0, T1), (T0, T2), (T0, T3), (T1, T0), (T1, T2), (T1, T3), (T2, T0), (T2, T1), (T2, T3), (T3, T0), (T3, T1), (T3, T2)); + fn permutations_2(self) -> Self::Output { + ((self.0.clone(), self.1.clone()), (self.0.clone(), self.2.clone()), (self.0.clone(), self.3.clone()), (self.1.clone(), self.0.clone()), (self.1.clone(), self.2.clone()), (self.1.clone(), self.3.clone()), (self.2.clone(), self.0.clone()), (self.2.clone(), self.1.clone()), (self.2.clone(), self.3.clone()), (self.3.clone(), self.0), (self.3.clone(), self.1), (self.3, self.2)) + } +} +impl TuplePermutations2 for (T0, T1, T2, T3, T4) { + type Output = ((T0, T1), (T0, T2), (T0, T3), (T0, T4), (T1, T0), (T1, T2), (T1, T3), (T1, T4), (T2, T0), (T2, T1), (T2, T3), (T2, T4), (T3, T0), (T3, T1), (T3, T2), (T3, T4), (T4, T0), (T4, T1), (T4, T2), (T4, T3)); + fn permutations_2(self) -> Self::Output { + ((self.0.clone(), self.1.clone()), (self.0.clone(), self.2.clone()), (self.0.clone(), self.3.clone()), (self.0.clone(), self.4.clone()), (self.1.clone(), self.0.clone()), (self.1.clone(), self.2.clone()), (self.1.clone(), self.3.clone()), (self.1.clone(), self.4.clone()), (self.2.clone(), self.0.clone()), (self.2.clone(), self.1.clone()), (self.2.clone(), self.3.clone()), (self.2.clone(), self.4.clone()), (self.3.clone(), self.0.clone()), (self.3.clone(), self.1.clone()), (self.3.clone(), self.2.clone()), (self.3.clone(), self.4.clone()), (self.4.clone(), self.0), (self.4.clone(), self.1), (self.4.clone(), self.2), (self.4, self.3)) + } +} +impl TuplePermutations2 for (T0, T1, T2, T3, T4, T5) { + type Output = ((T0, T1), (T0, T2), (T0, T3), (T0, T4), (T0, T5), (T1, T0), (T1, T2), (T1, T3), (T1, T4), (T1, T5), (T2, T0), (T2, T1), (T2, T3), (T2, T4), (T2, T5), (T3, T0), (T3, T1), (T3, T2), (T3, T4), (T3, T5), (T4, T0), (T4, T1), (T4, T2), (T4, T3), (T4, T5), (T5, T0), (T5, T1), (T5, T2), (T5, T3), (T5, T4)); + fn permutations_2(self) -> Self::Output { + ((self.0.clone(), self.1.clone()), (self.0.clone(), self.2.clone()), (self.0.clone(), self.3.clone()), (self.0.clone(), self.4.clone()), (self.0.clone(), self.5.clone()), (self.1.clone(), self.0.clone()), (self.1.clone(), self.2.clone()), (self.1.clone(), self.3.clone()), (self.1.clone(), self.4.clone()), (self.1.clone(), self.5.clone()), (self.2.clone(), self.0.clone()), (self.2.clone(), self.1.clone()), (self.2.clone(), self.3.clone()), (self.2.clone(), self.4.clone()), (self.2.clone(), self.5.clone()), (self.3.clone(), self.0.clone()), (self.3.clone(), self.1.clone()), (self.3.clone(), self.2.clone()), (self.3.clone(), self.4.clone()), (self.3.clone(), self.5.clone()), (self.4.clone(), self.0.clone()), (self.4.clone(), self.1.clone()), (self.4.clone(), self.2.clone()), (self.4.clone(), self.3.clone()), (self.4.clone(), self.5.clone()), (self.5.clone(), self.0), (self.5.clone(), self.1), (self.5.clone(), self.2), (self.5.clone(), self.3), (self.5, self.4)) + } +} +#[doc = "Permutation by 3"] +pub trait TuplePermutations3 { + type Output; + #[doc = "Permutation by 3"] + fn permutations_3(self) -> Self::Output; +} +impl TuplePermutations3 for (T0, T1, T2) { + type Output = ((T0, T1, T2), (T0, T2, T1), (T1, T0, T2), (T1, T2, T0), (T2, T0, T1), (T2, T1, T0)); + fn permutations_3(self) -> Self::Output { + ((self.0.clone(), self.1.clone(), self.2.clone()), (self.0.clone(), self.2.clone(), self.1.clone()), (self.1.clone(), self.0.clone(), self.2.clone()), (self.1.clone(), self.2.clone(), self.0.clone()), (self.2.clone(), self.0.clone(), self.1.clone()), (self.2, self.1, self.0)) + } +} diff --git a/tuples/src/lib.rs b/tuples/src/lib.rs index b934f4e..af1660a 100644 --- a/tuples/src/lib.rs +++ b/tuples/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![allow(unused_imports)] #[cfg(feature = "tuple_meta")] mod meta { @@ -107,7 +108,6 @@ mod shorthand { { $($t:tt)* } => { tuple_! { $($t)* } } } } -#[allow(unused_imports)] #[cfg(feature = "shorthand")] pub use shorthand::*; @@ -206,3 +206,8 @@ pub use sort::*; pub mod tuple_swap_n; #[cfg(any(all(feature = "tuple_swap_n", feature = "re-exports"), test, doc))] pub use tuple_swap_n::*; + +#[cfg(any(feature = "permutations", test, doc))] +pub mod permutations; +#[cfg(any(all(feature = "permutations", feature = "re-exports"), test, doc))] +pub use permutations::*; diff --git a/tuples/src/permutations.rs b/tuples/src/permutations.rs new file mode 100644 index 0000000..a0c52ab --- /dev/null +++ b/tuples/src/permutations.rs @@ -0,0 +1,21 @@ +include!("./gen/permutations.rs"); + +#[cfg(test)] +mod tests { + use super::TuplePermutations2; + + #[test] + fn test1() { + let a = (1, 2, 3); + let r = a.permutations_2(); + assert_eq!(r, ((1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2))); + } + + + #[test] + fn test2() { + let a = (1, '2', "3"); + let r = a.permutations_2(); + assert_eq!(r, ((1, '2'), (1, "3"), ('2', 1), ('2', "3"), ("3", 1), ("3", '2'))); + } +}