It provides multiple way to get permutation of data.
Easiest generic use case ```Rust use permutator::{CartesianProduct, Combination, Permutation}; let domains : &[&[i32]] = &[&[1, 2], &[3, 4, 5], &[6], &[7, 8], &[9, 10, 11]]; domains.cartprod().foreach(|cp| { // each cp will be &[&i32] with length equals to domains.len() which in this case 5
// It's k-permutation of size 3 over data.
cp.combination(3).for_each(|mut c| { // need mut
// each `c` is not &[&&i32]
// print the first 3-combination over data
println!("{:?}", c);
// start permute the 3-combination
c.permutation().for_each(|p| {
// each `p` is not &[&&&i32]
// print each permutation of the 3-combination.
println!("{:?}", p);
});
// It'll print the last 3-permutation again because permutation permute the value in place.
println!("{:?}", c);
})
});
Notice that each nested level get deeper reference.
If such behavior is undesired, use `copy` module.
Here's an example:
Rust
use permutator::copy::{CartesianProduct, Combination, Permutation};
let domains : &[&[i32]] = &[&[1, 2], &[3, 4, 5], &[6], &[7, 8], &[9, 10, 11]];
domains.cartprod().foreach(|cp| {
// each cp will be &[i32] with length equals to domains.len() which in this case 5
// It's k-permutation of size 3 over data.
cp.combination(3).for_each(|mut c| { // need mut
// each `c` is not &[i32]
// print the first 3-combination over data
println!("{:?}", c);
// start permute the 3-combination
c.permutation().for_each(|p| {
// each `p` is not &[i32]
// print each permutation of the 3-combination.
println!("{:?}", p);
});
// It'll print the last 3-permutation again because permutation permute the value in place.
println!("{:?}", c);
})
}); ```
copy
moduleThis crate split into two modules. One is root module which can be used in most of the case. Another is copy
module which require that the type implement Copy
trait. The root module always return value as a collection of &T
, except all heap permutaiton family. The copy
module always return value as a collection of T
.
K-Permutation
iterator family depends on HeapPermutation
iterator family but HeapPermutation
iterator family in root module doesn't require T
to implement Copy
trait while K-Permutation
iterator family require T
to implement Copy
trait.
It provides 2 functions to get a cartesian product or k-permutation: - getcartesianfor - getpermutationfor It also provide utilities functions like: - getcartesiansize - getpermutationsize
There are two distinct implementation to get cartesian product. - Iterator that return product - Function that call callback function to return product
This crate provides SelfCartesianProductIterator
, SelfCartesianProductCellIter
, and SelfCartesianProductRefIter
structs that implement
Iterator
, IteratorReset
, ExactSizeIterator
traits. Each struct serves different use cases:-
- SelfCartesianProductIterator
can be used in any case that performance is least concern.
- SelfCartesianProductCellIter
can be used in case performance is important as well as safety.
- SelfCartesianProductRefIter
can be used in case performance is critical and safety will be handle by caller.
Every structs implements IteratorReset
trait.
- use reset
function instead of creating a new Iterator everytime you need to re-iterate.
This crate provides CartesianProduct
trait which add function cart_prod
that return an Iterator to generate a Cartesian Product
over a set itself multiple times. The types that currently support are:
- (&'a [T], usize) - Generate cartesian product over 'first paramter' for 'second paramater' times.
- (&'a [T], usize, RcSelfCartesianProductRefIter
.
This crate provides 4 functions that serve different usecase.
- self_cartesian_product
function that return product as callback parameter
- self_cartesian_product_cell
function that return product into Rc
There are two distinct implementation to get cartesian product. - Iterator that return product - Function that call callback function to return product
This crate provides CartesianProductIterator
, CartesianProductCellIter
, and CartesianProductRefIter
structs that implement
Iterator
, IteratorReset
, ExactSizeIterator
traits. Each struct serves different use cases:-
- CartesianProductIterator
can be used in any case that performance is least concern.
- CartesianProductCellIter
can be used in case performance is important as well as safety.
- CartesianProductRefIter
can be used in case performance is critical and safety will be handle by caller.
Every structs implements IteratorReset
trait.
- use reset
function instead of creating a new Iterator everytime you need to re-iterate.
This crate provides CartesianProduct
trait and basic implementation various types such as generic slice of slices, generic Vec of slices, tuple of (&'a [&'a [T]], RcCartesianProductIterator
but on (&'a [&'a [T]], *mut [&'a T]) return CartesianProductRefIter
.
This crate provides 4 functions that serve different usecase.
- cartesian_product
function that return product as callback parameter
- cartesian_product_cell
function that return product into Rc
There are three distinct implementation to get k-combinations of n set.
- Iterator that return each combination on each iteration
- Trait that add function to slice, Vec, Rc This crate provides This crate provides This crate provide 4 functions that serve different usecase.
- There are three distinct implementation to get permutation.
- Iterator that do permutation on data
- Trait that add function to slice, Vec, etc.
- Function that call callback function to return each permutation This crate provides This crate provides This crate provide 3 functions that serve different usecase.
- There are three implementation to get k-permutations of n set.
- Iterator that return product
- Trait that add functionality to some specific tuple.
- Function that call callback function to return product This crate provides This crate provides This crate provide 4 functions that serve different usecase.
- Struct like Most of sharing result use interior mutability so that the function/struct only borrow the
sharing result. It'll mutably borrow only when it's going to mutate result and drop the mutable
borrow immediately before calling a callback or return result from iteration.
This mean that the result is also mutable on user side. However, doing so may result in undesired
behavior. For example: heappermutationcell function swap a pair of element inside Rc This crate provides two built-in methods to send result across thread. The two usecase is strongly
against each other in term of performance.
The callback with "_sync" suffix store borrowed result into Arc Another way is to use Iterator that return an owned value then clone that value on each thread.
This is much simpler to implement but require more memory. It'll simplify the scenario above to:
1. The iterator return new result.
2. It send notification with new data via channel to every threads.
The performance observed in uncontrolled test environment show that the iterator way
is faster than the callback by at least 50%. It's because all "unsafe_" prefix function and struct with To get into 'n' specific lexicographic permutation,
```Rust
use permutator::getcartesiansize; getcartesiansize(3, 2); // return 9.
getcartesiansize(3, 3); // return 27. use permutator::getcartesianfor; let nums = [1, 2, 3];
getcartesianfor(&nums, 2, 0); // Return Ok([1, 1])
getcartesianfor(&nums, 2, 3); // Return Ok([2, 1])
getcartesianfor(&nums, 2, 8); // Return Ok([3, 3])
getcartesianfor(&nums, 2, 9); // Return Err("Parameter use permutator::getpermutationsize; getpermutationsize(3, 2); // return = 6
getpermutationsize(4, 2); // return = 12 use permutator::getpermutationfor; let nums = [1, 2, 3, 4];
getpermutationfor(&nums, 2, 0); // return Result([1, 2])
getpermutationfor(&nums, 3, 0); // return Result([1, 2, 3])
getpermutationfor(&nums, 2, 5); // return Result([2, 4])
getpermutationfor(&nums, 2, 11); // return Result([4, 3])
getpermutationfor(&nums, 2, 12); // return Err("parameter x is outside a possible length")
getpermutationfor(&nums, 5, 0); // return Err("Insufficient number of object in parameters objects for given parameter degree")
``` To get cartesian product from 3 set of data.
```Rust
use permutator::cartesian_product; ``` The struct offer two ways to get a combination.
First it can be used as Iterator. Second
manually call next with borrowed mut variable that
will store the next combination.
```Rust
// Combination iterator
use permutator::GosperCombinationIterator;
use std::time::{Instant};
let data = [1, 2, 3, 4, 5];
let gosper = GosperCombinationIterator::new(&data, 3);
let mut counter = 0;
let timer = Instant::now(); for combination in gosper {
// uncomment a line below to print each combination
// println!("{}:{:?}", counter, combination);
counter += 1;
} println!("Total {} combinations in {:?}", counter, timer.elapsed());
let data = [1, 2, 3, 4, 5];
let mut counter = 0; let timer = Instant::now(); data.combination(3).for_each(|combination| {
// uncomment a line below to print each combination
// println!("{}:{:?}", counter, combination);
counter += 1;
} println!("Total {} combinations in {:?}", counter, timer.elapsed());
``` There's for permutated in permutator {
// println!("{}:{:?}", counter, permutated);
counter += 1;
} // or use iterator related functional approach like line below.
// permutator.intoiter().foreach(|permutated| {counter += 1;}); println!("Done {} permutations in {:?}", counter, timer.elapsed());
``` There's let data = &mut [1, 2, 3, 4];
let result = Rc::new(RefCell::new(data));
// print original data before permutation
// println!("0:{:?}", &*result.borrow());
let mut permutator = HeapPermutationCellIter::new(Rc::clone(&result));
let timer = Instant::now();
let mut counter = 1; for _ in permutator {
// uncomment the line below to print all possible permutation
// println!("{}:{:?}", counter, &*result.borrow());
counter += 1;
} println!("Done {} permutations in {:?}", counter, timer.elapsed());
let k = 3;
let data = &[1, 2, 3, 4, 5];
let mut result = vec![&data[0]; k];
let shared = Rc::new(RefCell::new(result.asmutslice())); let mut kperm = KPermutationCellIter::new(data, k, Rc::clone(&shared));
for _ in kperm {
// each permutation will be stored in data.cartprod().foreach(|p| {
// print all product like [1, 4], [1, 5], ...
println!("{:?}", p);
});
let k = 3;
let data = &[1, 2, 3, 4, 5];
let mut result = vec![&data[0]; k];
let shared = Rc::new(RefCell::new(result.asmutslice())); (data, k, Rc::clone(&shared)).permutation().foreach(|| {
// each permutation will be stored in In some circumstance, the combination result need to be shared but
the safe function don't allow you to share the result except copy/clone
the result for each share. When that's the case, using Iterator may answer
such situation. Another approach is to use Another way, if safety is less concern than performance, there's an
unsafe side implementation that take a mutable pointer to store result.
There's more thing to keep in mind than using struct with Example:
- unsafe callback function
```Rust
use permutator::unsafecombination;
let data = [1, 2, 3, 4, 5];
let r = 3;
let mut counter = 0;
let mut result = vec![&data[0]; r];
let resultptr = result.asmutslice() as *mut [&usize]; ``` An example showing the built-in feature that save new cartesian product into
Rc ``` This example generates a k-permutation and send it to multiple threads
by using KPermutation iterator. The main thread will keep generating a new k-permutation and send it to
every thread while all other threads read new k-permutation via channel.
In this example, it use sync_channel with size 0. It doesn't hold anything
inside the buffer. The sender will block until the receiver read the data.
```Rust
use permutator::KPermutation;
use std::sync::mpsc;
let k = 5;
let data : &[i32] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; ``` This example generates a k-permutation and send it to multiple threads by
using a callback approach kpermutationsync function. The main thread will keep generating a new k-permutation and send it to
every thread while all other threads read new k-permutation via channel.
In this example, it use syncchannel with size 0. It doesn't hold anything
inside the buffer. The sender will block until the receiver read the data.
```Rust
use std::sync::{Arc, RwLock};
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, SyncSender};
fn startkpermutationprocess<'a>(data : &'a[i32], curresult : Arc ``` Version 0.2.0 has major overhaul on entire crate to make use case more consistent on each other functionalities. There are now only 2 major distinct styles. 1. Callback function 2. Iterator object. The Iterator object has 2 sub-style. 1. Plain Example:
New }
}
```Iterator
GosperCombinationIterator
, GosperCombinationCellIter
, and GosperCombinationRefIter
structs that implement
IntoIterator
traits. Each struct serves different use cases:-
- GosperCombinationIterator
can be used in any case that performance is least concern.
- GosperCombinationCellIter
can be used in case performance is important as well as safety.
- GosperCombinationRefIter
can be used in case performance is critical and safety will be handle by caller.
- These struct isn't implement Iterator itself but it still provide reset
function to reset state of the object instead of creating a new object.
It's also provides CombinationIterator
, CombinationCellIter
, and CombinationRefIter
structs that implement
Iterator
, IteratorReset
, ExactSizeIterator
traits which is an Iterator
representation of each respectively GosperCombination
structs
Every structs implements IteratorReset
trait.
- use reset
function instead of creating a new Iterator everytime you need to re-iterate.Trait
Combination
trait and basic implementation various types such as generic slice, generic Vec, tuple of (&'a [T], RcCombinationIterator
but on (&'a [T], * mut[&'a T]) return CombinationRefIter
.
Note:
- It doesn't return GosperCombination
family of structs but return a direct Iterator
implemented object.Callback function
combination
function that return product as callback parameter
- combination_cell
function that return product into RcGet a permutation from data
Iterator
HeapPermutationIterator
, HeapPermutationCellIter
, and HeapPermutationRefIter
structs that implement
Iterator
, IteratorReset
, ExactSizeIterator
traits. Each struct serves different use cases:-
- HeapPermutationIterator
can be used in any case that performance is least concern.
- HeapPermutationCellIter
can be used in case performance is important as well as safety.
- HeapPermutationRefIter
can be used in case performance is critical and safety will be handle by caller.
Every structs implements IteratorReset
trait.
- use reset
function instead of creating a new Iterator everytime you need to re-iterate.Trait
Permutation
trait and basic implementation various types such as generic slice, generic Vec, tuple of (&'a mut[T], Rcpermutation()
function to it and return required iterator based on type of data. For example on generic Vec return HeapPermutationIterator
but on (&'a mut [T], RcCallback function
heap_permutation
function that return product as callback parameter
- heap_permutation_cell
function that return product into RcGet a k-permutation from data
Iterator
KPermutationIterator
, KPermutationCellIter
, and KPermutationRefIter
structs that implement
Iterator
, IteratorReset
, ExactSizeIterator
traits. Each struct serves different use cases:-
- KPermutationIterator
can be used in any case that performance is least concern.
- KPermutationCellIter
can be used in case performance is important as well as safety.
- KPermutationRefIter
can be used in case performance is critical and safety will be handle by caller.
Every structs implements IteratorReset
trait.
- use reset
function instead of creating a new Iterator everytime you need to re-iterate.Trait
Permutation
trait that can be used to perform on tuple of (&'a [T], usize), tuple of (&'a [T], usize, RcKPermutationIterator
but on (&'a [T], usize, *mut [&'a T]) return KPermutationRefIter
.Callback function
k_permutation
function that return product as callback parameter
- k_permutation_cell
function that return product into RcNotes
Struct with
RefIter
and CellIter
suffix return empty Item on each IterationCartesianProductIterator
, CombinationIterator
, HeapPermutationIterator
, KPermutationIterator
return fresh new Vec on each iteration. All other structs that have other way to return value will return empty tuple on each iteration. For example, CartesianProductCellIter
, CombinationRefIter
, HeapPermutationCellIter
, and KPermutationRefIter
all return empty tuple on each iteration. It return result via parameter specified when instantiate an object. For example, method new
on CartesianProductCellIter
require RcCellIter
overwrite the result of previous iteration on every iteration. If every result from each iteration need to be kept, consider using non-suffix version. For example, instead of using KPermutationRefIter
and clone/copy every result into Vec, consider using KPermutationIterator
instead.Performance concern
copy
and root module performance is roughly equivalent.copy
performance will depend on the implementation of Copy
trait.
CellIter
and RefIter
suffix.CellIter
suffix method.
The return owned value method is slowest but most versatile. It's about 700%-1000% slower than using
CellIter
suffix object. However, it is still faster than using standard callback function then
convert it to owned value to share result.
Mutating result is dangerous
Send result to other thread is complicated
Unsafe way is fastest and hardest
RefIter
suffix return result throught mutable pointer that
make it has lowest cost to send result back. It leave everything else to user to do the work.
To use it, make sure that the memory is return when it no longer use, synchronization, initialization
is properly done. The original variable owner outlive both user and generator.Example
Get a permutation at specific point examples
i
is out of bound")
getcartesianfor(&nums, 4, 0); // Return Err("Parameter degree
cannot be larger than size of objects")Cartesian product of multiple sets of data
cartesian_product(&[&[1, 2, 3], &[4, 5, 6], &[7, 8, 9]], |product| {
println!("{:?}", product);
});
Or do it in iterative style
Rust
use permutator::CartesianProductIterator
use std::time::Instant;
let data : &[&[usize]] = &[&[1, 2, 3], &[4, 5, 6], &[7, 8, 9]];
let cart = CartesianProductIterator::new(&data);
let mut counter = 0;
let timer = Instant::now();for p in cart {
// println!("{:?}", p);
counter += 1;
}
assert_eq!(data.iter().fold(1, |cum, domain| {cum * domain.len()}), counter);
println!("Total {} products done in {:?}", counter, timer.elapsed());
Import trait then skipping all object instantiation altogether.
Rust
use std::time::Instant;
use permutator::CartesianProduct;
let data : &[&[usize]] = &[&[1, 2], &[3, 4, 5, 6], &[7, 8, 9]];
let mut counter = 0;
let timer = Instant::now();data.cart_prod.for_each(|p| {
// println!("{:?}", p);
counter += 1;
});
assert_eq!(data.iter().fold(1, |cum, domain| {cum * domain.len()}), counter);
println!("Total {} products done in {:?}", counter, timer.elapsed());
Combination Iterator examples
Rust
use permutator::Combination;
use std::time::{Instant};Iterator style permutation example
HeapPermutationIterator
and KPermutationIterator
struct that can do
permutation. Below is an example of HeapPermutationIterator
.
```Rust
use permutator::HeapPermutationIterator;
use std::time::{Instant};
let data = &mut [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
println!("0:{:?}", data);
let mut permutator = HeapPermutationIterator::new(data);
let timer = Instant::now();
let mut counter = 1;Iterator into Rc
HeapPermutationCellIter
and KPermutationCellIter
struct that offer
such functionality. Below is an example of HeapPermutationCellIter
```Rust
use permutator::HeapPermutationCellIter;
use std::cell::RefCell;
use std::rc::Rc;
use std::time::{Instant};
The `KPermutationCellIter` example show below
Rust
use permutator::KPermutationCellIter;
use std::cell::RefCell;
use std::rc::Rc;shared
println!("{:?}", &*shared.borrow());
}
```Traits that add new function to various types
CartesianProduct
trait add cart_prod
function.
The function take no parameter. The function return the same Iterator that also return by
the provided struct
so it can be used like this example
```Rust
use permutator::CartesianProduct;
let data : &[&[i32]]= &[&[1, 2, 3], &[4, 5]];
or
Rust
use permutator::CartesianProduct;
let data : &[&[i32]]= &[&[1, 2, 3], &[4, 5]];
let mut result = vec![&data[0][0]; data.len()];
let shared = Rc::new(RefCell::new(result.asmutslice()));
// shared can be owned by anyone who want to get cartesian product.
(&data, Rc::clone(&shared)).cartprod().foreach(|| {
// print all product like [1, 4], [1, 5], ...
println!("{:?}", &*shared.borrow());
// and notify all owner of shared object so they know that new product is available.
});
`Combination` trait add `combination` function.
The function take 1 parameter. It's a size of combination frame, AKA `k`, `r`, etc.
The function return the same Iterator that also return by
the [provided struct](#iterator-1)
so it can be used like [this example](#combination-iterator-examples)
Rust
use permutator::Combination;
let data = [1, 2, 3, 4, 5];
data.combination(3).foreach(|comb| {
// print all combination like [1, 2, 3], [1, 2, 4], ...
println!("{:?}", comb);
});
or
Rust
use permutator::Combination;
let data = [1, 2, 3, 4, 5];
let k = 3;
let mut result = vec![&data[0]; k];
let shared = Rc::new(RefCell::new(result.asmutslice()));
// shared can be owned by anyone who want to get combinations.
(&data, Rc::clone(&shared)).combination(k).foreach(|| {
// print all combination like [1, 2, 3], [1, 2, 4], ...
println!("{:?}", &shared.borrow());
// and notify all owner of shared object so they know that new combination is available.
});
`Permutation` trait add `permutation` function.
It permute the `[T]`, `Vec<T>`, or Rc<RefCell<&mut [T]>> in place.
The function return the same Iterator that also return by the either
this [provided struct](#iterator-2) or this [provided struct](#iterator-3)
depending on what types does this method is called upon
so it can be used like [this example](#iterator-style-permutation-example)
or [this example](#iterator-into-rcrefcell) or following example:
Rust
use permutator::Permutation;
let mut data = [1, 2, 3];
data.permutation().for_each(|p| {
// print all the permutation.
println!("{:?}", p);
});
// The data
at this point will also got permuted.
// It'll print the last permuted value twice.
println!("{:?}", data);
Rust
use permutator::Permutation;
let mut data = [1, 2, 3];
let shared = Rc::new(RefCell::new(&mut data));
// shared can be owned by anyone that want to get a permutation
Rc::clone(&shared).permutation().for_each(|_| {
// print all the permutation.
println!("{:?}", &shared.borrow());
// and notify all owner of shared object so they know that new permutation is available.
});
// The same goes as previous example, the data inside shared on every owner will now has last permuted value.
or k-permutation into Rc<RefCell<>>
Rust
use permutator::KPermutationCellIter;
use std::cell::RefCell;
use std::rc::Rc;shared
println!("{:?}", &*shared.borrow());
});
```Unsafe way for faster share result
CellIer
suffix struct or callback function
with _cell
suffix. As long as each iteration doesn't reuse previous
result and result owner treat result as immutable data then it's safe
to use this approach.CellIter
suffix
and callback function _cell
suffix. For example:
1. Pointer need to outlive the entire operation
2. The object that pointer is pointed to need to be release.
3. Result synchronization, both in single and multiple thread(s).
4. ...
5. All other unsafe Rust conditions appliedunsafe {
unsafe_combination(&data, r, result_ptr, || {
println!("{:?}", result);
counter += 1;
});
}
assert_eq!(counter, divide_factorial(data.len(), data.len() - r) / factorial(r));
- unsafe Iterator object
Rust
use permutator::GosperCombinationRefIter;
let data = [1, 2, 3, 4, 5];
let r = 3;
let mut counter = 0;
let mut result = vec![&data[0]; r];
let resultptr = result.asmut_slice() as *mut [&usize];unsafe {
let comb = GosperCombinationRefIter::new(&data, r, result_ptr);
for _ in comb {
println!("{:?}", result);
counter += 1;
});
}
assert_eq!(counter, divide_factorial(data.len(), data.len() - r) / factorial(r));
Share with multiple object from callback function
use permutator::cartesian_product_cell;
trait Consumer {
fn consume(&self);
}
struct Worker1<'a, T : 'a> {
data : Rc<RefCell<&'a mut[&'a T]>>
}
impl<'a, T : 'a + Debug> Consumer for Worker1<'a, T> {
fn consume(&self) {
println!("Work1 has {:?}", self.data);
}
}
struct Worker2<'a, T : 'a> {
data : Rc<RefCell<&'a mut[&'a T]>>
}
impl<'a, T : 'a + Debug> Consumer for Worker2<'a, T> {
fn consume(&self) {
println!("Work2 has {:?}", self.data);
}
}
fn start_cartesian_product_process<'a>(data : &'a[&'a[i32]], cur_result : Rc<RefCell<&'a mut [&'a i32]>>, consumers : Vec<Box<Consumer + 'a>>) {
cartesian_product_cell(data, cur_result, || {
consumers.iter().for_each(|c| {
c.consume();
})
});
}
let data : &[&[i32]] = &[&[1, 2], &[3, 4, 5], &[6]];
let mut result = vec![&data[0][0]; data.len()];
let shared = Rc::new(RefCell::new(result.as_mut_slice()));
let worker1 = Worker1 {
data : Rc::clone(&shared)
};
let worker2 = Worker2 {
data : Rc::clone(&shared)
};
let consumers : Vec<Box<Consumer>> = vec![Box::new(worker1), Box::new(worker2)];
start_cartesian_product_process(data, shared, consumers);
Iterator that send data to other threads
// workter thread 1
let (t1_send, t1_recv) = mpsc::sync_channel::<Option<Vec<&i32>>>(0);
thread::spawn(move || {
while let Some(c) = t1_recv.recv().unwrap() {
let result : Vec<&i32> = c;
println!("Thread1: {:?}", result);
}
println!("Thread1 is done");
});
// worker thread 2
let (t2_send, t2_recv) = mpsc::sync_channel::<Option<Vec<&i32>>>(0);
thread::spawn(move || {
while let Some(c) = t2_recv.recv().unwrap() {
let result : Vec<&i32> = c;
println!("Thread2: {:?}", result);
}
println!("Thread2 is done");
});
let channels = vec![t1_send, t2_send];
// main thread that generate result
thread::spawn(move || {
use std::time::Instant;
let timer = Instant::now();
let mut counter = 0;
let kperm = KPermutation::new(data, k);
kperm.into_iter().for_each(|c| {
channels.iter().for_each(|t| {t.send(Some(c.to_owned())).unwrap();});
counter += 1;
});
channels.iter().for_each(|t| {t.send(None).unwrap()});
println!("Done {} combinations in {:?}", counter, timer.elapsed());
}).join().unwrap();
Callback function send data to other thread
for _ in 0..notifier.len() {
release_recv.recv().unwrap(); // block until all thread reading data notify on read completion
}
counter += 1;
});
notifier.iter().for_each(|n| {n.send(None).unwrap()}); // notify every thread that there'll be no more data.
println!("Done {} combinations with 2 workers in {:?}", counter, timer.elapsed());
}
let k = 5;
let data = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let result = vec![&data[0]; k];
let result_sync = Arc::new(RwLock::new(result));
// workter thread 1
let (t1_send, t1_recv) = mpsc::sync_channel::<Option<()>>(0);
let (main_send, main_recv) = mpsc::sync_channel(0);
let t1_local = main_send.clone();
let t1_dat = Arc::clone(&result_sync);
thread::spawn(move || {
while let Some(_) = t1_recv.recv().unwrap() {
let result : &Vec<&i32> = &*t1_dat.read().unwrap();
// println!("Thread1: {:?}", result);
t1_local.send(()).unwrap(); // notify generator thread that reference is no longer neeed.
}
println!("Thread1 is done");
});
// worker thread 2
let (t2_send, t2_recv) = mpsc::sync_channel::<Option<()>>(0);
let t2_dat = Arc::clone(&result_sync);
let t2_local = main_send.clone();
thread::spawn(move || {
while let Some(_) = t2_recv.recv().unwrap() {
let result : &Vec<&i32> = &*t2_dat.read().unwrap();
// println!("Thread2: {:?}", result);
t2_local.send(()).unwrap(); // notify generator thread that reference is no longer neeed.
}
println!("Thread2 is done");
});
// main thread that generate result
thread::spawn(move || {
start_k_permutation_process(data, result_sync, k, vec![t1_send, t2_send], main_recv);
}).join().unwrap();
Breaking change from 0.1.6 to 0.2.0
Iterator
style. 2. Shared Iterator
style. The shared Iterator
style has both safe and unsafe kind of share which is similar to callback style counterpart. It need to rename every structs. It add one more trait and some more types.
More detail on breaking change:
- An iterator style next_into_cell
has been refactored into their own struct. Now it can be used like simple Iterator with slightly different way to return value.
- A mimic iterator style next
that took &mut[&T]
parameter has been refactored into their own struct. Now it can be used like simple Iterator with slightly different way to return value.
- CartesianProduct
struct is renamed to CartesianProductIterator
- HeapPermutation
struct is renamed to HeapPermutationIterator
- GosperCombination
struct is renamed to GosperCombinationIterator
- KPermutation
struct is renamed to KPermutationIterator
- Combination
and Permutation
traits now use associated type combinator
and permutator
respectively to define the struct that will be used to perform combination/permutation on slice/array/Vec and RcItem
defined in Iterator
thought. The trait now take <'a> lifetime parameter and no longer take generic type T
. The combination
function change signature from combination(&mut self)
to combination(&'a mut self)
. The permutation
function change signature from permutation(&mut self)
to permutation(&'a mut self)
.Migration guide from 0.1.6 to 0.2.0
unsafe
to use. Following is the list of such structs.
next_into_cell
function now moved into it own iterator struct that have suffix "CellIter" in its' name. Following is the list of such structs.
CartesianProduct
struct is renamed to CartesianProductIterator
HeapPermutation
struct is renamed to HeapPermutationIterator
GosperCombination
struct is renamed to GosperCombinationIterator
KPermutation
struct is renamed to KPermutationIterator
Combination
and Permutation
traits need to define the associated type as well as change combination
and permutation
function signature from taking &self
to &'a self
and &mut self
to &'a mut self
respectively.Permutation
trait now look like this.
``Rust
// instead of this old implementation
// impl Permutation<T> for [T] {
// fn permutation(&mut self) -> HeapPermutation<T> {
// HeapPermutation {
// c : vec![0; self.len],
// data : self,
// i : 0
// }
// }
// }
// now it become..
impl<'a, T> Permutation<'a> for [T] where T : 'a {
type Permutator = HeapPermutation<'a, T>; // This struct implement
Iterator`fn permutation(&'a mut self) -> HeapPermutation<T> {
HeapPermutation {
c : vec![0; self.len()],
data : self,
i : 0
}
}
The added complexity make this trait applicable to wider type.
Here's new implemention on `Rc<RefCell<&mut [T]>>` which return `HeapPermutationCell`.
Rust
impl<'a, T> Permutation<'a> for RcIterator
fn permutation(&'a mut self) -> HeapPermutationCell<T> {
HeapPermutationCell {
c : vec![0; self.borrow().len()],
data : Rc::clone(self),
i : 0
}
}