perm_vec/lib.rs
1/* ************************************************************************ **
2** This file is part of rsp2, and is licensed under EITHER the MIT license **
3** or the Apache 2.0 license, at your option. **
4** **
5** http://www.apache.org/licenses/LICENSE-2.0 **
6** http://opensource.org/licenses/MIT **
7** **
8** Be aware that not all of rsp2 is provided under this permissive license, **
9** and that the project as a whole is licensed under the GPL 3.0. **
10** ************************************************************************ */
11
12use std::fmt;
13
14/// Represents a reordering operation on an array.
15///
16/// See the [`Permute`] trait for more information.
17#[derive(Debug, Clone, PartialEq, Eq, Hash)]
18pub struct Perm {
19 inv: PermVec,
20}
21
22// This is a Perm stored in the form that's easiest to reason about.
23// (documented in `Perm::from_vec`)
24//
25// It exists solely for clarity of implementation, because implementing
26// `Perm::shift_right` as `inv: self.inv.prefix_shift_left()` says it a lot
27// better than any comment I could write above the inlined method body.
28//
29// Basically, method bodies on Perm describe the relationship between
30// the perm and its inverse, while method bodies on PermVec do the real work.
31#[derive(Clone, PartialEq, Eq, Hash)]
32struct PermVec( // PermVec<Src, Dest>
33 Vec<usize>, // Indexed<Dest, Vec<Src>>
34);
35
36impl fmt::Debug for PermVec {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 fmt::Debug::fmt(&self.0, f)
39 }
40}
41
42/// An error which indicates that a permutation vector is invalid.
43///
44/// A permutation vector `vec` is valid if and only if it contains one copy of every
45/// index in the range `0..vec.len()`.
46#[derive(Debug)]
47pub struct InvalidPermutationError { _private: () }
48
49impl fmt::Display for InvalidPermutationError {
50 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
51 fmt::Display::fmt("Tried to construct an invalid permutation.", f)
52 }
53}
54
55impl Perm {
56 /// Construct the identity perm of a given length.
57 pub fn eye(n: usize) -> Perm
58 { Perm { inv: PermVec::eye(n) } }
59
60 /// Get the length of the permutation.
61 pub fn len(&self) -> usize
62 { self.inv.0.len() }
63
64 /// Compute the `Perm` that, when applied to the input slice, would (stably) sort it.
65 pub fn argsort<T: Ord>(xs: &[T]) -> Perm
66 { Perm { inv: PermVec::argsort(xs).inverted() } }
67
68 /// Compute a `Perm` that, when applied to the input slice, would sort it. (not necessarily stably)
69 pub fn argsort_unstable<T: Ord>(xs: &[T]) -> Perm
70 { Perm { inv: PermVec::argsort_unstable(xs).inverted() } }
71
72 /// Construct a perm. Useful for literals in unit tests.
73 ///
74 /// The representation accepted by this is comparable to indexing with an
75 /// integer array in numpy. If the `k`th element of the permutation vector
76 /// is `value`, then applying the permutation will *pull* the data at index
77 /// `value` into index `k`.
78 ///
79 /// This performs O(n log n) validation on the data to verify that it
80 /// satisfies the invariants of Perm. It also inverts the perm (an O(n)
81 /// operation).
82 pub fn from_vec(vec: Vec<usize>) -> Result<Perm, InvalidPermutationError>
83 { Ok(Perm { inv: PermVec::from_vec(vec)? }.inverted()) }
84
85 /// Construct a perm from the vector internally used to represent it.
86 ///
87 /// The format taken by this method is actually **the inverse** of the format
88 /// accepted by `from_vec`. If the `k`th element of the permutation vector is
89 /// `value`, then applying the permutation will *push* the data at index `k`
90 /// over to index `value`. This format is generally trickier to think about,
91 /// but is superior to the `from_vec` representation in terms of efficiency.
92 ///
93 /// This performs O(n log n) validation on the data to verify that it
94 /// satisfies the invariants of `Perm`.
95 pub fn from_raw_inv(inv: Vec<usize>) -> Result<Perm, InvalidPermutationError>
96 { Ok(Perm { inv: PermVec::from_vec(inv)? }) }
97
98 /// No-op constructor. Still performs checking in debug builds.
99 ///
100 /// # Safety
101 ///
102 /// `inv` must contain every element in `(0..inv.len())`,
103 /// or else the behavior is undefined.
104 pub unsafe fn from_raw_inv_unchecked(inv: Vec<usize>) -> Perm
105 { Perm { inv: PermVec(inv).debug_validated() } }
106
107 /// Construct a permutation of length `a.len() + b.len()`.
108 ///
109 /// Mathematically speaking, this computes the "direct sum" of two permutations.
110 ///
111 /// The inserted elements will be shifted by this permutation's length,
112 /// so that they operate on an entirely independent set of data from
113 /// the existing elements.
114 pub fn append_mut(&mut self, other: &Perm)
115 {
116 // (a direct sum of inverses is the inverse of the direct sum)
117 self.inv.append_mut(&other.inv);
118 }
119
120 /// Construct a random permutation of the given length.
121 pub fn random(n: usize) -> Perm
122 {
123 use rand::Rng;
124
125 let mut inv: Vec<_> = (0..n).collect();
126 rand::thread_rng().shuffle(&mut inv);
127 Perm { inv: PermVec(inv) }
128 }
129
130 /// Recover the vector representation of the permutation.
131 ///
132 /// See [`Perm::from_vec`] for more details about this representation.
133 ///
134 /// This has a runtime cost of O(n), because [`Perm`] does not actually store this
135 /// vector. See [`Perm::into_raw_inv`] for a constant-time alternative.
136 pub fn into_vec(self) -> Vec<usize>
137 { self.inverted().inv.0 }
138
139 /// Obtain the vector that is internally used to represent the permutation.
140 ///
141 /// This representation is actually the inverse of the representation produced by
142 /// [`Perm::into_vec`]. See [`Perm::from_raw_inv`] for more information.
143 pub fn into_raw_inv(self) -> Vec<usize>
144 { self.inv.0 }
145
146 /// Get the inverse of this permutation.
147 #[must_use = "not an in-place operation"]
148 pub fn inverted(&self) -> Perm
149 {
150 // (the inverse of the inverse is... you know...)
151 Perm { inv: self.inv.inverted() }
152 }
153
154 // (this might sound niche, but it's not like we can safely expose `&mut [u32]`,
155 // so what's the harm in having a niche method?)
156 //
157 /// Compose with the permutation that shifts elements forward (performing `self` first)
158 ///
159 /// To construct the shift permutation itself, use `Perm::eye(n).shift_right(amt)`.
160 pub fn shift_right(self, amt: usize) -> Self
161 { Perm { inv: self.inv.prefix_shift_left(amt) } }
162
163 /// Compose with the permutation that shifts elements backward (performing `self` first)
164 ///
165 /// To construct the shift permutation itself, use `Perm::eye(n).shift_left(amt)`.
166 pub fn shift_left(self, amt: usize) -> Self
167 { Perm { inv: self.inv.prefix_shift_right(amt) } }
168
169 /// Compose with the permutation that shifts elements to the right by a signed offset.
170 pub fn shift_signed(self, n: isize) -> Self
171 {
172 if n < 0 {
173 assert_ne!(n, std::isize::MIN, "(exasperated sigh)");
174 self.shift_left((-n) as usize)
175 } else {
176 self.shift_right(n as usize)
177 }
178 }
179
180 /// Apply the permutation to an index. O(1).
181 ///
182 /// Calling this on the indices contained in a sparse-format data structure will
183 /// produce the same indices as if the corresponding dense-format data structure
184 /// were permuted.
185 ///
186 /// # Panics
187 ///
188 /// Panics if `i` is out of bounds for the permutation length.
189 pub fn permute_index(&self, i: usize) -> usize {
190 // F.Y.I. this method is **literally the entire reason** that we store the inverse.
191 self.inv.0[i]
192 }
193
194 /// Construct the outer product of self and `slower`, with `self`
195 /// being the fast (inner) index.
196 ///
197 /// The resulting `Perm` will permute blocks of size `self.len()`
198 /// according to `slower`, and will permute elements within each
199 /// block by `self`.
200 pub fn with_outer(&self, slower: &Perm) -> Perm
201 {
202 // the inverse of the outer product is the outer product of inverses
203 Perm { inv: self.inv.with_outer(&slower.inv) }
204 }
205
206 /// Construct the outer product of self and `faster`, with `self`
207 /// being the slow (outer) index.
208 pub fn with_inner(&self, faster: &Perm) -> Perm
209 { faster.with_outer(self) }
210
211 /// Compute the permutation that applies this permutation `exp` times in a row.
212 ///
213 /// This uses exponentiation by squaring to run in `O(log(exp))` time.
214 pub fn pow_unsigned(&self, mut exp: u64) -> Perm {
215 // Exponentiation by squaring (permutations form a monoid)
216
217 // NOTE: there's plenty of room to optimize the number of heap
218 // allocations here
219 let mut acc = Perm::eye(self.len());
220 let mut base = self.clone();
221 while exp > 0 {
222 if (exp & 1) == 1 {
223 acc = acc.permuted_by(&base);
224 }
225 base = base.clone().permuted_by(&base);
226 exp /= 2;
227 }
228 acc
229 }
230
231 /// Compute the permutation that applies this permutation `exp` times in a row.
232 ///
233 /// This version of the function takes a signed value, so that negative values can produce
234 /// powers of the inverse.
235 ///
236 /// This uses exponentiation by squaring to run in `O(log(exp))` time.
237 pub fn pow_signed(&self, exp: i64) -> Perm {
238 if exp < 0 {
239 assert_ne!(exp, std::i64::MIN); // technically possible to support but... come on, man
240 self.inverted().pow_unsigned((-exp) as u64)
241 } else {
242 self.pow_unsigned(exp as u64)
243 }
244 }
245}
246
247impl PermVec {
248 fn eye(n: usize) -> PermVec
249 { PermVec((0..n).collect()) }
250
251 fn argsort<T: Ord>(xs: &[T]) -> PermVec
252 {
253 let mut perm: Vec<_> = (0..xs.len()).collect();
254 perm.sort_by(|&a, &b| xs[a].cmp(&xs[b]));
255 PermVec(perm)
256 }
257
258 fn argsort_unstable<T: Ord>(xs: &[T]) -> PermVec
259 {
260 let mut perm: Vec<_> = (0..xs.len()).collect();
261 perm.sort_unstable_by(|&a, &b| xs[a].cmp(&xs[b]));
262 PermVec(perm)
263 }
264
265 fn from_vec(vec: Vec<usize>) -> Result<PermVec, InvalidPermutationError>
266 {
267 if !Self::validate_data(&vec) {
268 return Err(InvalidPermutationError { _private: () });
269 }
270 Ok(PermVec(vec))
271 }
272
273 // Checks invariants required by Perm for unsafe code.
274 #[must_use = "doesn't assert"]
275 fn validate_data(xs: &[usize]) -> bool {
276 let mut vec = xs.to_vec();
277 vec.sort();
278 vec.into_iter().eq(0..xs.len())
279 }
280
281 fn debug_validated(self) -> PermVec {
282 debug_assert!(PermVec::validate_data(&self.0));
283 self
284 }
285
286 fn append_mut(&mut self, other: &Self)
287 {
288 let offset = self.0.len();
289 self.0.extend(other.0.iter().map(|&i| i + offset));
290 debug_assert!(PermVec::validate_data(&self.0));
291 }
292
293 #[must_use = "not an in-place operation"]
294 fn inverted(&self) -> Self
295 {
296 let mut inv = vec![::std::usize::MAX; self.0.len()]; // [Src] -> Dest
297 for (i, &x) in self.0.iter().enumerate() { // i: Dest, x: Src
298 inv[x] = i;
299 }
300 PermVec(inv).debug_validated()
301 }
302
303 // The perm that does `self`, then shifts right.
304 #[allow(unused)]
305 fn postfix_shift_right(mut self, amt: usize) -> PermVec
306 {
307 let n = self.0.len();
308 self.0.rotate_right(amt % n);
309 self.debug_validated()
310 }
311
312 // The perm that does `self`, then shifts left.
313 #[allow(unused)]
314 fn postfix_shift_left(mut self, amt: usize) -> PermVec
315 {
316 let n = self.0.len();
317 self.0.rotate_left(amt % n);
318 self.debug_validated()
319 }
320
321 // The perm that shifts left, then applies `self`
322 fn prefix_shift_left(mut self, amt: usize) -> PermVec {
323 // Add amt to each value.
324 let n = self.0.len();
325 let amt = amt % n;
326 for x in &mut self.0 {
327 *x = (*x + amt) % n;
328 }
329 self.debug_validated()
330 }
331
332 // The perm that shifts right, then applies `self`
333 fn prefix_shift_right(self, amt: usize) -> PermVec {
334 // Subtract amt from each value.
335 // ...or rather, shift left by `(-amt) mod len`
336 //
337 // (technically, this puts it into the range `[1, len]` instead of `[0, len)`
338 // due to a silly edge case, but that doesn't matter)
339 let len = self.0.len();
340 self.prefix_shift_left(len - amt % len)
341 }
342
343 fn with_outer(&self, slower: &PermVec) -> PermVec
344 {
345 assert!(self.0.len().checked_mul(slower.0.len()).is_some());
346
347 let mut perm = Vec::with_capacity(self.0.len() * slower.0.len());
348
349 for &block_index in &slower.0 {
350 let offset = self.0.len() * block_index;
351 perm.extend(self.0.iter().map(|&x| x + offset));
352 }
353 PermVec(perm).debug_validated()
354 }
355
356 // Perm that applies self then other.
357 fn then(&self, other: &PermVec) -> PermVec
358 {
359 assert_eq!(self.0.len(), other.0.len(), "Incorrect permutation length");
360
361 let mut out = vec![::std::usize::MAX; self.0.len()];
362
363 for (out_i, &self_i) in other.0.iter().enumerate() {
364 out[out_i] = self.0[self_i];
365 }
366
367 PermVec(out).debug_validated()
368 }
369}
370
371impl Perm {
372 /// Flipped group operator, which composes left-to-right.
373 ///
374 /// Very simply. `a.then(b) == b.of(a)`. The flipped order can feel more natural
375 /// when using method syntax, or if you are dealing with matrix equations written
376 /// in a row-centric formalism.
377 ///
378 /// Additionally, it has a straightforward relation to the group action:
379 /// ```text
380 /// x.permuted_by(a).permuted_by(b) == x.permuted_by(a.then(b))
381 /// ```
382 pub fn then(&self, other: &Perm) -> Perm
383 {
384 // The inverses compose in reverse.
385 Perm { inv: other.inv.then(&self.inv) }
386 }
387
388 /// Conventional group operator, which composes right-to-left.
389 pub fn of(&self, other: &Perm) -> Perm
390 { other.then(self) }
391}
392
393/// Trait for applying a permutation operation.
394///
395/// Impls of `Permute` do not always necessarily apply the permutation directly to
396/// vectors contained in the type. For instance, data in a sparse format will likely
397/// use the permutation to transform stored indices.
398///
399/// As a conceptual tool, it can help to consider indices as having distinct types
400/// (e.g. vertex indices, component indices; or more specific things like "indices of
401/// rows when sorted by column 2"); in this model, `Perm` can be thought of as being
402/// parametrized over two index types, where `Perm<Src, Dest>` transforms data indexed
403/// by type `Src` into data indexed by type `Dest`. Again, this is just a conceptual
404/// tool; `Perm` does not actually have these type parameters!
405/// (More about this in [this blog post](https://exphp.github.io/blog/2018/07/30/that-weekend-i-wasted-on-newtyped-indices.html))
406///
407/// # Laws
408///
409/// All implementations of `Permute` must satisfy the following properties,
410/// which give `Permute::permuted_by` the qualities of a group action.
411/// (whose group operator is, incidentally, also `Permute::permuted_by`!)
412///
413/// * **Identity:**
414/// ```text
415/// data.permuted_by(Perm::eye(data.len())) == data
416/// ```
417/// * **Compatibility:**
418/// ```text
419/// data.permuted_by(a).permuted_by(b) == data.permuted_by(a.permuted_by(b))
420/// ```
421///
422/// When envisioning `Perm` as generic over `Src` and `Dest` types, it could
423/// perhaps be said that `Perm`s are the morphisms of a category. (brushing
424/// aside issues of mismatched length)
425pub trait Permute: Sized {
426 // awkward name, but it makes it makes two things clear
427 // beyond a shadow of a doubt:
428 // - The receiver gets permuted, not the argument.
429 // (relevant when Self is Perm)
430 // - The permutation is not in-place.
431 fn permuted_by(self, perm: &Perm) -> Self;
432}
433
434// (module to protect from lollipop model; the unsafety here
435// is extremely localized)
436mod unsafe_impls {
437 use super::*;
438
439 pub(super) fn inv_permute_to_new_vec<T>(vec: Vec<T>, inv: &PermVec) -> Vec<T> {
440 let mut out = Vec::with_capacity(vec.len());
441 inv_permute_to_mut_vec(vec, inv, &mut out);
442 out
443 }
444
445 pub(super) fn inv_permute_to_mut_vec<T>(mut vec: Vec<T>, inv: &PermVec, out: &mut Vec<T>) {
446 assert_eq!(
447 vec.len(), inv.0.len(),
448 "Incorrect permutation length",
449 );
450
451 out.clear();
452 out.reserve_exact(inv.0.len());
453
454 //------------------------------------------------
455 // You are now entering a PANIC FREE ZONE
456
457 { // scope ptrs so we can reason about them
458 let vec_ptr = vec.as_ptr();
459 let out_ptr = out.as_mut_ptr();
460
461 // a perm holds indices into the data vec, so the inverse holds indices into `out`.
462 for (vec_i, &out_i) in inv.0.iter().enumerate() {
463 // SAFETY:
464 //
465 // * vec_i < vec.len() because:
466 // * vec_i comes from the standard library implementation of `impl Iterator for std::iter::Enumerate`,
467 // and is thus guaranteed to be `< inv.0.len()`.
468 // * We asserted earlier that `inv.0.len() == vec.len()`.
469 //
470 // * vec[vec_i] will not be double-dropped, because:
471 // * we perform `vec.set_len(0)` after this loop.
472 // * we cannot possibly panic before this occurs.
473 let value = unsafe { vec_ptr.offset(vec_i as isize).read() };
474
475 // SAFETY:
476 //
477 // * out_i < out.capacity() because:
478 // * A privacy-protected invariant of PermVec guarantees that `out_i < inv.0.len()`.
479 // * We called `Vec::reserve_exact` to ensure that `inv.0.len() <= out.capacity()`.
480 let dest_ptr = unsafe { out_ptr.offset(out_i as isize) };
481 unsafe { dest_ptr.write(value) };
482 }
483 }
484
485 // Don't drop the original items, but do allow the original
486 // vec to fall out of scope so the memory can be freed.
487
488 // SAFETY:
489 //
490 // * All elements in out[0..vec.len()] are initialized because:
491 // * A privacy-protected invariant of PermVec guarantees that, in the above `for` loop,
492 // every index from 0..vec.len() will have appeared exactly once as `out_i`.
493 unsafe { out.set_len(vec.len()); }
494 unsafe { vec.set_len(0); }
495
496 // Thank you for flying with us. You may now PANIC!
497 //------------------------------------------------
498 }
499}
500
501impl<T> Permute for Vec<T> {
502 fn permuted_by(self, perm: &Perm) -> Vec<T>
503 { self::unsafe_impls::inv_permute_to_new_vec(self, &perm.inv) }
504}
505
506// `Permute` doubles as the group operator.
507// (think of it as matrix multiplication in the matrix representation)
508impl Permute for PermVec {
509 fn permuted_by(self, perm: &Perm) -> PermVec
510 { PermVec(self.0.permuted_by(perm)) }
511}
512
513impl Permute for Perm {
514 fn permuted_by(self, other: &Perm) -> Perm
515 { self.then(other) }
516}
517
518// rsp2-tasks needs this
519impl<T: Clone> Permute for std::rc::Rc<[T]> {
520 fn permuted_by(self, perm: &Perm) -> Self
521 {
522 // this could be done with less copying...
523 // (though it absolutely has to make at least one full copy)
524 let vec = self.to_vec(); // an O(n) copy
525 let vec = vec.permuted_by(perm); // O(n) work
526 let slice = vec.into_boxed_slice();
527 slice.into() // another O(n) copy to embed refcount
528 }
529}
530
531impl<T: Permute + Clone> Permute for std::rc::Rc<T> {
532 fn permuted_by(self, perm: &Perm) -> Self {
533 Box::new((*self).clone().permuted_by(perm)).into()
534 }
535}
536
537#[cfg(test)]
538#[deny(unused)]
539mod tests {
540 use super::*;
541
542 use self::drop_pusher::DropPusher;
543 mod drop_pusher {
544 use std::rc::Rc;
545 use std::cell::RefCell;
546
547 /// Helper for testing panic/drop safety.
548 pub struct DropPusher<T: Copy>(Rc<RefCell<Vec<T>>>, T);
549
550 impl<T: Copy + 'static> DropPusher<T> {
551 /// Create a shared vector, and a `new` function which constructs
552 /// `DropPushers` tied to that vector.
553 pub fn new_trial() -> (Rc<RefCell<Vec<T>>>, Box<dyn Fn(T) -> DropPusher<T>>)
554 {
555 let history = Rc::new(RefCell::new(vec![]));
556 let new = {
557 let history = history.clone();
558 Box::new(move |x| DropPusher(history.clone(), x))
559 };
560 (history, new)
561 }
562 }
563
564 impl<T: Copy> Drop for DropPusher<T> {
565 fn drop(&mut self) {
566 self.0.borrow_mut().push(self.1);
567 }
568 }
569 }
570
571 #[test]
572 fn inverse()
573 {
574 let perm = Perm::random(20);
575 let inv = perm.inverted();
576
577 assert_eq!(perm.clone().permuted_by(&inv), Perm::eye(20));
578 assert_eq!(inv.permuted_by(&perm), Perm::eye(20));
579 }
580
581 #[test]
582 fn inverse_is_argsort()
583 {
584 let perm = Perm::random(20);
585 assert_eq!(
586 Perm::argsort(&perm.clone().into_vec()).into_vec(),
587 perm.inverted().into_vec(),
588 );
589 }
590
591 #[test]
592 fn invalid() {
593 assert!(matches!(
594 Perm::from_vec(vec![0, 1, 3, 3]),
595 Err(InvalidPermutationError {..}),
596 ));
597
598 assert!(matches!(
599 Perm::from_vec(vec![1, 2, 3]),
600 Err(InvalidPermutationError {..}),
601 ));
602 }
603
604 #[test]
605 #[should_panic(expected = "permutation length")]
606 fn incompatible() {
607 // another requirement for the Vec impl's safety
608 let _ = vec![4, 2, 1].permuted_by(&Perm::eye(2));
609 }
610
611 #[test]
612 fn drop_safety() {
613 let (drop_history, dp) = DropPusher::new_trial();
614 {
615 let vec = vec![dp(0), dp(1), dp(2), dp(3), dp(4)];
616
617 let vec2 = vec.permuted_by(&Perm::from_vec(vec![3, 1, 0, 4, 2]).unwrap());
618 assert_eq!(drop_history.borrow().len(), 0);
619
620 drop(vec2);
621 assert_eq!(drop_history.borrow().len(), 5);
622 }
623 assert_eq!(drop_history.borrow().len(), 5);
624 }
625
626 #[test]
627 fn argsort_is_stable()
628 {
629 use rand::Rng;
630
631 // a long vector of only two unique values; a prime target for stability checks
632 let n = 300;
633 let values: Vec<_> = (0..n).map(|_| rand::thread_rng().gen_range(0, 2)).collect();
634
635 let perm = Perm::argsort(&values);
636 let permuted_indices = (0..n).collect::<Vec<_>>().permuted_by(&perm);
637 let permuted_values = values.permuted_by(&perm);
638
639 let error = format!("not your lucky day, Mister one-in-{:e}", 2f64.powi(n));
640 let first_one = permuted_values.iter().position(|&x| x == 1).expect(&error);
641
642 let is_strictly_sorted = |xs: &[_]| xs.windows(2).all(|w| w[0] < w[1]);
643 assert!(is_strictly_sorted(&permuted_indices[..first_one]));
644 assert!(is_strictly_sorted(&permuted_indices[first_one..]));
645
646 let error = format!("DEFINITELY not your lucky day, Mister one-in-{}-factorial!!", n);
647 assert!(!is_strictly_sorted(&permuted_indices[..]), "{}", error);
648 }
649
650 #[test]
651 fn associativity()
652 {
653 let xy = Perm::from_vec(vec![1, 0, 2]).unwrap();
654 let zx = Perm::from_vec(vec![2, 1, 0]).unwrap();
655 let xyzx = Perm::from_vec(vec![2, 0, 1]).unwrap();
656 assert_eq!(xy.clone().permuted_by(&zx), xyzx);
657 assert_eq!(xy.then(&zx), xyzx);
658 assert_eq!(zx.of(&xy), xyzx);
659 assert_eq!(
660 vec![0,1,2].permuted_by(&xy).permuted_by(&zx),
661 vec![0,1,2].permuted_by(&xyzx),
662 );
663 assert_eq!(
664 vec![0,1,2].permuted_by(&xy).permuted_by(&zx),
665 vec![2,0,1],
666 );
667
668 for _ in 0..10 {
669 use rand::Rng;
670
671 let mut rng = rand::thread_rng();
672 let n = rng.gen_range(10, 20);
673 let s = b"abcdefghijklmnopqrstuvwxyz"[..n].to_vec();
674 let a = Perm::random(n);
675 let b = Perm::random(n);
676 let c = Perm::random(n);
677 let bc = b.clone().permuted_by(&c);
678 assert_eq!(
679 a.clone().permuted_by(&b).permuted_by(&c),
680 a.clone().permuted_by(&bc),
681 "compatibility, for Self = Perm (a.k.a. associativity)",
682 );
683 assert_eq!(
684 a.inv.clone().permuted_by(&b).permuted_by(&c),
685 a.inv.clone().permuted_by(&bc),
686 "compatibility, for Self = PermVec",
687 );
688 assert_eq!(
689 s.clone().permuted_by(&b).permuted_by(&c),
690 s.clone().permuted_by(&bc),
691 "compatibility, for Self = Vec",
692 );
693 }
694 }
695
696 #[test]
697 fn append()
698 {
699 let mut a = Perm::from_vec(vec![1, 0]).unwrap();
700 let b = Perm::from_vec(vec![1, 2, 0]).unwrap();
701 a.append_mut(&b);
702 assert_eq!(
703 vec![00, 01, /* -- */ 10, 11, 12].permuted_by(&a),
704 vec![01, 00, /* -- */ 11, 12, 10],
705 );
706 }
707
708 #[test]
709 fn outer()
710 {
711 let use_outer = |a, b| {
712 let a = Perm::from_vec(a).unwrap();
713 let b = Perm::from_vec(b).unwrap();
714 let xs: Vec<_> =
715 (0..b.len()).flat_map(|slow| {
716 (0..a.len()).map(move |fast| 10 * slow + fast)
717 }).collect();
718 xs.permuted_by(&a.with_outer(&b))
719 };
720
721 assert_eq!(
722 use_outer(
723 vec![1, 0, 2, 3],
724 vec![1, 2, 0],
725 ),
726 vec![
727 11, 10, 12, 13,
728 21, 20, 22, 23,
729 01, 00, 02, 03,
730 ],
731 );
732
733 // empty perms
734 assert_eq!(use_outer(vec![1, 0], vec![]), vec![]);
735
736 assert_eq!(use_outer(vec![], vec![1, 0]), vec![]);
737 }
738
739 #[test]
740 fn shift() {
741 assert_eq!(
742 vec![0, 1, 2, 3, 4, 5].permuted_by(&Perm::eye(6).shift_right(8)),
743 vec![4, 5, 0, 1, 2, 3],
744 );
745 assert_eq!(
746 vec![0, 1, 2, 3, 4, 5].permuted_by(&Perm::eye(6).shift_left(8)),
747 vec![2, 3, 4, 5, 0, 1],
748 );
749 assert_eq!(
750 vec![0, 1, 2, 3, 4, 5].permuted_by(&Perm::eye(6).shift_signed(8)),
751 vec![4, 5, 0, 1, 2, 3],
752 );
753 assert_eq!(
754 vec![0, 1, 2, 3, 4, 5].permuted_by(&Perm::eye(6).shift_signed(-8)),
755 vec![2, 3, 4, 5, 0, 1],
756 );
757 // potentially dumb edge case
758 assert_eq!(
759 vec![0, 1, 2, 3, 4, 5].permuted_by(&Perm::eye(6).shift_signed(6)),
760 vec![0, 1, 2, 3, 4, 5],
761 );
762 assert_eq!(
763 vec![0, 1, 2, 3, 4, 5].permuted_by(&Perm::eye(6).shift_signed(-6)),
764 vec![0, 1, 2, 3, 4, 5],
765 );
766 }
767
768 #[test]
769 fn pow_unsigned() {
770 for &len in &[0, 1, 4, 20] {
771 for _ in 0..5 {
772 let perm = Perm::random(len);
773 for &exp in &[0, 1, 4, 20, 21] {
774 let original = b"abcdefghijklmnopqrstuvwxyz"[..len as usize].to_owned();
775
776 let mut brute_force = original.clone();
777 for _ in 0..exp {
778 brute_force = brute_force.permuted_by(&perm);
779 }
780
781 let fast = original.permuted_by(&perm.pow_unsigned(exp));
782 assert_eq!(fast, brute_force);
783 }
784 }
785 }
786 }
787
788 #[test]
789 /// ```rust
790 /// // IMPORTANT: If you modify this test, UPDATE THE README!!!
791 ///
792 /// use perm_vec::{Perm, Permute};
793 ///
794 /// fn main() {
795 /// // The vec that permutes "abcd" into "bcda".
796 /// let perm_shl = Perm::from_vec(vec![1, 2, 3, 0]).unwrap();
797 /// assert_eq!(vec![0, 10, 20, 30].permuted_by(&perm_shl), vec![10, 20, 30, 0]);
798 ///
799 /// // The permutation that reverses a vector
800 /// let perm_rev = Perm::from_vec((0..4).rev().collect()).unwrap();
801 /// assert_eq!(vec![0, 10, 20, 30].permuted_by(&perm_rev), vec![30, 20, 10, 0]);
802 ///
803 /// // Let's compose them!
804 /// let perm_comp_1 = perm_shl.then(&perm_rev); // this one shifts, then reverses
805 /// let perm_comp_2 = perm_rev.then(&perm_shl); // this one reverses, then shifts
806 /// assert_eq!(vec![0, 10, 20, 30].permuted_by(&perm_comp_1), vec![0, 30, 20, 10]);
807 /// assert_eq!(vec![0, 10, 20, 30].permuted_by(&perm_comp_2), vec![20, 10, 0, 30]);
808 /// }
809 /// ```
810 fn _readme_doctest() {}
811}