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}