strided/
lib.rs

1//! Strided slices.
2//!
3//! This library provides two types `Stride` and `MutStride` as
4//! generalised forms of `&[T]` and `&mut [T]` respectively, where the
5//! elements are regularly spaced in memory, but not necessarily
6//! immediately adjacently.
7//!
8//! For example, given an underlying array `[1, 2, 3, 4, 5]`, the
9//! elements `[1, 3, 5]` are a strided slice with stride 2, and
10//! `[1, 4]` has stride 3. Any slice can be regarded as a strided slice
11//! with stride 1.
12//!
13//! This provides functionality through which one can safely and
14//! efficiently manipulate every `n`th element of a slice (even a
15//! mutable one) as close as possible to it being a conventional
16//! slice. This releases one from worries about stride bookkeeping,
17//! aliasing of `&mut` or any `unsafe` code.
18//!
19//! # Quick start
20//!
21//! The work-horse functions are `.substrides(n)` and
22//! `.substrides_mut(n)`, which return an iterator across a series of
23//! `n` new strided slices (shared and mutable, respectively), each of
24//! which points to every `n`th element, and each of which starts at
25//! the next successive offset. For example, the following has
26//! `n = 3`.
27//!
28//! ```rust
29//! use strided::MutStride;
30//!
31//! let mut v = [1u8, 2, 3, 4, 5];
32//! let mut all = MutStride::new(&mut v);
33//!
34//! let mut substrides = all.substrides_mut(3);
35//!
36//! let a = substrides.next().unwrap();
37//! let b = substrides.next().unwrap();
38//! let c = substrides.next().unwrap();
39//! assert!(substrides.next().is_none()); // there was exactly 3.
40//!
41//! assert_eq!(a, MutStride::new(&mut [1, 4]));
42//! assert_eq!(b, MutStride::new(&mut [2, 5]));
43//! assert_eq!(c, MutStride::new(&mut [3]));
44//! ```
45//!
46//! The common case of `n = 2` has an abbreviation `substrides2`
47//! (resp. `substrides2_mut`), which takes the liberty of returns a
48//! tuple rather than an iterator to make direct destructuring
49//! work. Continuing with the values above, `left` and `right` point
50//! to alternate elements, starting at index `0` and `1` of their
51//! parent slice respectively.
52//!
53//! ```rust
54//! # use strided::MutStride;
55//! # let mut v = [1u8, 2, 3, 4, 5];
56//! # let mut all = MutStride::new(&mut v);
57//! let (left, right) = all.substrides2_mut();
58//!
59//! assert_eq!(left, MutStride::new(&mut [1, 3, 5]));
60//! assert_eq!(right, MutStride::new(&mut [2, 4]));
61//! ```
62//!
63//! A lot of the conventional slice functionality is available, such
64//! as indexing, iterators and slicing.
65//!
66//! ```rust
67//! # use strided::MutStride;
68//! # let mut v = [1u8, 2, 3, 4, 5];
69//! # let mut all = MutStride::new(&mut v);
70//! let (mut left, right) = all.substrides2_mut();
71//! assert_eq!(left[2], 5);
72//! assert!(right.get(10).is_none()); // out of bounds
73//!
74//! left[2] += 10;
75//! match left.get_mut(0) {
76//!     Some(val) => *val -= 1,
77//!     None => {}
78//! }
79//!
80//! assert_eq!(right.iter().fold(0, |sum, a| sum + *a), 2 + 4);
81//! for val in left.iter_mut() {
82//!     *val /= 2
83//! }
84//! ```
85//!
86//! ## Ownership and `reborrow`
87//!
88//! `MutStride` has a method `reborrow` which has signature
89//!
90//! ```rust,ignore
91//! impl<'a, T> MutStride<'a, T> {
92//!     pub fn reborrow<'b>(&'b mut self) -> MutStride<'b, T> { ... }
93//! }
94//! ```
95//!
96//! That is, it allows temporarily viewing a strided slices as one
97//! with a shorter lifetime. This method is key because many of the
98//! methods on `MutStride` take `self` by-value and so consume
99//! ownership... which is rather unfortunate if one wants to use a
100//! strided slice multiple times.
101//!
102//! The temporary returned by `reborrow` can be used with the
103//! consuming methods, which allows the parent slice to continuing
104//! being used after that temporary has disappeared. For example, all
105//! of the splitting and slicing methods on `MutStride` consume
106//! ownership, and so `reborrow` is necessary there to continue using,
107//! in this case, `left`.
108//!
109//! ```rust
110//! # use strided::MutStride;
111//! # let mut v = [1u8, 2, 3, 4, 5];
112//! # let mut all = MutStride::new(&mut v);
113//! let (mut left, right) = all.substrides2_mut();
114//! assert_eq!(left.reborrow().slice_mut(1, 3), MutStride::new(&mut [3, 5]));
115//! assert_eq!(left.reborrow().slice_from_mut(2), MutStride::new(&mut [5]));
116//! assert_eq!(left.reborrow().slice_to_mut(2), MutStride::new(&mut [1, 3]));
117//!
118//! // no reborrow:
119//! assert_eq!(right.split_at_mut(1),
120//!            (MutStride::new(&mut [2]), MutStride::new(&mut [4])));
121//! // println!("{}", right); // error: use of moved value `right`.
122//! ```
123//!
124//! These contortions are necessary to ensure that `&mut`s cannot
125//! alias, while still maintaining flexibility: leaving elements with
126//! the maximum possible lifetime (i.e. that of the non-strided slices
127//! which they lie in). Theoretically they are necessary with
128//! `&mut []` too, but the compiler inserts implicit reborrows and so
129//! one rarely needs to do them manually.
130//!
131//! In practice, one should only need to insert `reborrow`s if the
132//! compiler complains about the use of a moved value.
133//!
134//! The shared `Stride` is equivalent to `&[]` and only handles `&`
135//! references, making ownership transfer and `reborrow` unnecessary,
136//! so all its methods act identically to those on `&[]`.
137//!
138//! # Example
139//!
140//! The [fast Fourier transform
141//! (FFT)](https://en.wikipedia.org/wiki/Fast_Fourier_transform) is a
142//! signal processing algorithm that performs a discrete Fourier
143//! transform (DFT) of length `n` in `O(n log n)` time. A DFT breaks a
144//! waveform into the sum of sines and cosines, and is an important
145//! part of many other algorithms due to certain nice properties of
146//! the Fourier transform.
147//!
148//! The first FFT algorithm was the [Cooley-Tukey
149//! algorithm](https://en.wikipedia.org/wiki/Cooley-Tukey_FFT_algorithm). The
150//! decimation-in-time variant works by computing the FFT of
151//! equal-length subarrays of equally spaced elements and then
152//! combining these together into the desired result. This sort of
153//! spacing is exactly the striding provided by this library, and
154//! hence this library can be used to create an FFT algorithm in a
155//! very natural way.
156//!
157//! Below is an implementation of the radix-2 case, that is, when the
158//! length `n` is a power of two. In this case, only two strided
159//! subarrays are necessary: exactly the alternating ones provided by
160//! `substrides2`. Note the use of `reborrow` to allow `start` and
161//! `end` to be used for the recursive `fft` calls and then again
162//! later in the loop.
163//!
164//! ```rust
165//! # #![allow(unstable)]
166//! extern crate strided;
167//! extern crate num; // https://github.com/rust-lang/num
168//! use std::f64;
169//! use num::complex::{Complex, Complex64};
170//! use strided::{MutStride, Stride};
171//!
172//! /// Writes the forward DFT of `input` to `output`.
173//! fn fft(input: Stride<Complex64>, mut output: MutStride<Complex64>) {
174//!     // check it's a power of two.
175//!     assert!(input.len() == output.len() && input.len().count_ones() == 1);
176//!
177//!     // base case: the DFT of a single element is itself.
178//!     if input.len() == 1 {
179//!         output[0] = input[0];
180//!         return
181//!     }
182//!
183//!     // split the input into two arrays of alternating elements ("decimate in time")
184//!     let (evens, odds) = input.substrides2();
185//!     // break the output into two halves (front and back, not alternating)
186//!     let (mut start, mut end) = output.split_at_mut(input.len() / 2);
187//!
188//!     // recursively perform two FFTs on alternating elements of the input, writing the
189//!     // results into the first and second half of the output array respectively.
190//!     fft(evens, start.reborrow());
191//!     fft(odds, end.reborrow());
192//!
193//!     // exp(-2πi/N)
194//!     let twiddle = Complex::from_polar(&1.0, &(-2.0 * f64::consts::PI / input.len() as f64));
195//!
196//!     let mut factor = Complex::new(1., 0.);
197//!
198//!     // combine the subFFTs with the relations:
199//!     //   X_k       = E_k + exp(-2πki/N) * O_k
200//!     //   X_{k+N/2} = E_k - exp(-2πki/N) * O_k
201//!     for (even, odd) in start.iter_mut().zip(end.iter_mut()) {
202//!         let twiddled = factor * *odd;
203//!         let e = *even;
204//!
205//!         *even = e + twiddled;
206//!         *odd = e - twiddled;
207//!         factor = factor * twiddle;
208//!     }
209//! }
210//!
211//! fn main() {
212//!     let a = [Complex::new(2., 0.), Complex::new(1., 0.),
213//!              Complex::new(2., 0.), Complex::new(1., 0.)];
214//!     let mut b = [Complex::new(0., 0.); 4];
215//!
216//!     fft(Stride::new(&a), MutStride::new(&mut b));
217//!     println!("forward: {:?} -> {:?}", &a, &b);
218//! }
219//! ```
220//!
221//! The above definitely has complexity `O(n log n)`, but it has a
222//! much larger constant factor than an optimised library like
223//! [FFTW](http://www.fftw.org/). (Strictly speaking `output` does not
224//! need to be a strided slice, since it is never split into
225//! alternating elements.)
226
227//#![feature(core)]
228#![cfg_attr(all(test, feature = "unstable"), feature(test))]
229
230#[cfg(all(test, feature = "unstable"))] extern crate test;
231
232pub use base::{Items, MutItems};
233
234pub use mut_::Stride as MutStride;
235pub use mut_::Substrides as MutSubstrides;
236
237pub use imm::Stride as Stride;
238pub use imm::Substrides as Substrides;
239
240
241pub use traits::{Strided, MutStrided};
242
243#[cfg(test)]
244mod common_tests;
245
246mod base;
247mod mut_;
248mod imm;
249mod traits;
250
251#[cfg(all(test, feature = "unstable"))]
252mod bench {
253    use super::Stride;
254    use test::Bencher as B;
255    use test;
256
257    const N: usize = 100;
258
259    #[bench]
260    fn iter_slice(b: &mut B) {
261        let v = (0..N).collect::<Vec<_>>();
262        b.iter(|| {
263            test::black_box(&v);
264            for e in v.iter() { test::black_box(e); }
265        })
266    }
267
268    #[bench]
269    fn iter_step_1(b: &mut B) {
270        let v = (0..N).collect::<Vec<_>>();
271        let s = Stride::new(&*v);
272        b.iter(|| {
273            test::black_box(&s);
274            for e in s.iter() { test::black_box(e); }
275        })
276    }
277
278    #[bench]
279    fn iter_step_13(b: &mut B) {
280        let v = (0..13 * N).collect::<Vec<_>>();
281        let s = Stride::new(&*v);
282        let s = s.substrides(13).next().unwrap();
283        b.iter(|| {
284            test::black_box(&s);
285            for e in s.iter() { test::black_box(e); }
286        })
287    }
288}