numeric_array/
lib.rs

1//! # `numeric-array`
2//!
3//! [![crates.io](https://img.shields.io/crates/v/numeric-array.svg)](https://crates.io/crates/numeric-array)
4//! [![Documentation](https://docs.rs/numeric-array/badge.svg)](https://docs.rs/numeric-array)
5//! [![MIT/Apache-2 licensed](https://img.shields.io/crates/l/numeric-array.svg)](./LICENSE-Apache)
6//!
7//! `numeric-array` is a wrapper around
8//! [`generic-array`](https://github.com/fizyk20/generic-array)
9//! that adds efficient numeric trait implementations, designed
10//! to encourage LLVM to autovectorize expressions into SIMD
11//! instructions and perform compile-time evaluation.
12//!
13//! All stable `core::ops` traits are implemented for `NumericArray` itself,
14//! plus the thin `NumericConstant` type, which is required to
15//! differeniate constant values from `NumericArray` itself.
16//!
17//! Additionally, most of `num_traits` are implemented,
18//! including `Num` itself. So you can even use a whole array as a generic number.
19//!
20//! Example:
21//!
22//! ```rust
23//! use num_traits::Float;
24//! use numeric_array::{NumericArray, narr};
25//!
26//! # #[cfg(feature = "std")]
27//! fn main() {
28//!     let a = narr![1.0, 2.0, 3.0, 4.0];
29//!     let b = narr![5.0, 6.0, 7.0, 8.0];
30//!     let c = narr![9.0, 1.0, 2.0, 3.0];
31//!
32//!     // Compiles to a single vfmadd213ps instruction on my machine
33//!     let d = a.mul_add(b, c);
34//!
35//!     assert_eq!(d, narr![14.0, 13.0, 23.0, 35.0]);
36//! }
37//!
38//! # #[cfg(not(feature = "std"))] fn main() {}
39//! ```
40//!
41//! When used with `RUSTFLAGS = "-C opt-level=3 -C target-cpu=native"`,
42//! then Rust and LLVM are smart enough to autovectorize almost all operations
43//! into SIMD instructions, or even just evaluate them at compile time.
44//! The above example is actually evaluated at compile time, so if you
45//! were to view the assembly it would show the result only.
46//!
47//! This is ideal for situations where simple component-wise operations are required for arrays.
48//!
49
50#![deny(missing_docs)]
51#![no_std]
52
53extern crate num_traits;
54
55pub extern crate generic_array;
56
57pub use generic_array::{typenum, ArrayLength};
58
59#[cfg(feature = "serde1")]
60extern crate serde;
61
62use core::{cmp, ptr, slice};
63
64use core::borrow::{Borrow, BorrowMut};
65use core::ops::{Deref, DerefMut, Index, IndexMut};
66use core::ops::{Range, RangeFrom, RangeFull, RangeTo};
67
68use core::iter::FromIterator;
69
70use core::fmt::{Debug, Formatter, Result as FmtResult};
71
72use generic_array::functional::*;
73use generic_array::sequence::*;
74use generic_array::{GenericArray, GenericArrayIter};
75
76#[cfg(feature = "serde1")]
77mod impl_serde;
78
79pub mod geometry;
80pub mod impls;
81pub mod simd;
82
83/// A numeric wrapper for a `GenericArray`, allowing for easy numerical operations
84/// on the whole sequence.
85///
86/// This has the added bonus of allowing SIMD optimizations for almost all operations
87/// when compiled with `RUSTFLAGS = "-C opt-level=3 -C target-cpu=native"`
88///
89/// For example, adding together four-element `NumericArray`'s will result
90/// in a single SIMD instruction for all elements at once.
91#[repr(transparent)]
92pub struct NumericArray<T, N: ArrayLength>(GenericArray<T, N>);
93
94/// Sugar for `NumericArray::new(arr![...])`
95///
96/// ```ignore
97/// #[macro_use]
98/// extern crate generic_array;
99/// ```
100///
101/// is required to use this, as it still uses the `arr!` macro internally.
102#[macro_export]
103macro_rules! narr {
104    ($($t:tt)*) => {
105        $crate::NumericArray::new($crate::generic_array::arr!($($t)*))
106    }
107}
108
109unsafe impl<T, N: ArrayLength> GenericSequence<T> for NumericArray<T, N> {
110    type Length = N;
111    type Sequence = Self;
112
113    #[inline(always)]
114    fn generate<F>(f: F) -> Self
115    where
116        F: FnMut(usize) -> T,
117    {
118        NumericArray(GenericArray::generate(f))
119    }
120}
121
122/// This is required to allow `NumericArray` to be operated on by both other `NumericArray`
123/// instances and constants, with generic types,
124/// because some type `U` supercedes `NumericArray<U, N>`
125///
126/// As a result, constants must be wrapped in this totally
127/// transparent wrapper type to differentiate the types to Rust.
128#[derive(Debug, Clone, Copy)]
129#[repr(transparent)]
130pub struct NumericConstant<T>(pub T);
131
132/// Creates a new `NumericConstant` from the given expression.
133#[macro_export]
134macro_rules! nconstant {
135    ($value:expr) => {
136        $crate::NumericConstant($value)
137    };
138}
139
140impl<T> Deref for NumericConstant<T> {
141    type Target = T;
142
143    fn deref(&self) -> &T {
144        &self.0
145    }
146}
147
148impl<T> DerefMut for NumericConstant<T> {
149    fn deref_mut(&mut self) -> &mut T {
150        &mut self.0
151    }
152}
153
154impl<T: Debug, N: ArrayLength> Debug for NumericArray<T, N> {
155    fn fmt(&self, f: &mut Formatter) -> FmtResult {
156        f.debug_tuple("NumericArray").field(&self.0).finish()
157    }
158}
159
160impl<X, T, N: ArrayLength> From<X> for NumericArray<T, N>
161where
162    X: Into<GenericArray<T, N>>,
163{
164    fn from(x: X) -> NumericArray<T, N> {
165        NumericArray::new(x.into())
166    }
167}
168
169impl<T: Clone, N: ArrayLength> Clone for NumericArray<T, N> {
170    fn clone(&self) -> NumericArray<T, N> {
171        NumericArray(self.0.clone())
172    }
173}
174
175impl<T: Copy, N: ArrayLength> Copy for NumericArray<T, N> where N::ArrayType<T>: Copy {}
176
177impl<T, N: ArrayLength> Deref for NumericArray<T, N> {
178    type Target = [T];
179
180    fn deref(&self) -> &Self::Target {
181        self.as_slice()
182    }
183}
184
185impl<T, N: ArrayLength> DerefMut for NumericArray<T, N> {
186    fn deref_mut(&mut self) -> &mut Self::Target {
187        self.as_mut_slice()
188    }
189}
190
191impl<T, U, N: ArrayLength> PartialEq<NumericArray<U, N>> for NumericArray<T, N>
192where
193    T: PartialEq<U>,
194{
195    fn eq(&self, rhs: &NumericArray<U, N>) -> bool {
196        **self == **rhs
197    }
198}
199
200impl<T, U, N: ArrayLength> PartialEq<GenericArray<U, N>> for NumericArray<T, N>
201where
202    T: PartialEq<U>,
203{
204    fn eq(&self, rhs: &GenericArray<U, N>) -> bool {
205        **self == **rhs
206    }
207}
208
209impl<T, N: ArrayLength> cmp::Eq for NumericArray<T, N> where T: cmp::Eq {}
210
211impl<T, N: ArrayLength> PartialOrd<Self> for NumericArray<T, N>
212where
213    T: PartialOrd,
214{
215    #[inline]
216    fn partial_cmp(&self, rhs: &Self) -> Option<cmp::Ordering> {
217        PartialOrd::partial_cmp(&self.0, &rhs.0)
218    }
219
220    #[inline]
221    fn lt(&self, rhs: &Self) -> bool {
222        PartialOrd::lt(&self.0, &rhs.0)
223    }
224
225    #[inline]
226    fn le(&self, rhs: &Self) -> bool {
227        PartialOrd::le(&self.0, &rhs.0)
228    }
229
230    #[inline]
231    fn gt(&self, rhs: &Self) -> bool {
232        PartialOrd::gt(&self.0, &rhs.0)
233    }
234
235    #[inline]
236    fn ge(&self, rhs: &Self) -> bool {
237        PartialOrd::ge(&self.0, &rhs.0)
238    }
239}
240
241impl<T, N: ArrayLength> PartialOrd<GenericArray<T, N>> for NumericArray<T, N>
242where
243    T: PartialOrd,
244{
245    #[inline]
246    fn partial_cmp(&self, rhs: &GenericArray<T, N>) -> Option<cmp::Ordering> {
247        PartialOrd::partial_cmp(&self.0, rhs)
248    }
249
250    #[inline]
251    fn lt(&self, rhs: &GenericArray<T, N>) -> bool {
252        PartialOrd::lt(&self.0, rhs)
253    }
254
255    #[inline]
256    fn le(&self, rhs: &GenericArray<T, N>) -> bool {
257        PartialOrd::le(&self.0, rhs)
258    }
259
260    #[inline]
261    fn gt(&self, rhs: &GenericArray<T, N>) -> bool {
262        PartialOrd::gt(&self.0, rhs)
263    }
264
265    #[inline]
266    fn ge(&self, rhs: &GenericArray<T, N>) -> bool {
267        PartialOrd::ge(&self.0, rhs)
268    }
269}
270
271impl<T, N: ArrayLength> cmp::Ord for NumericArray<T, N>
272where
273    T: cmp::Ord,
274{
275    #[inline]
276    fn cmp(&self, rhs: &Self) -> cmp::Ordering {
277        cmp::Ord::cmp(&self.0, &rhs.0)
278    }
279}
280
281impl<T, N: ArrayLength> NumericArray<T, N> {
282    /// Creates a new `NumericArray` instance from a `GenericArray` instance.
283    ///
284    /// Example:
285    ///
286    /// ```
287    /// #[macro_use]
288    /// extern crate generic_array;
289    /// extern crate numeric_array;
290    ///
291    /// use numeric_array::NumericArray;
292    ///
293    /// fn main() {
294    ///     let arr = NumericArray::new(arr![1, 2, 3, 4]);
295    ///
296    ///     println!("{:?}", arr); // Prints 'NumericArray([1, 2, 3, 4])'
297    /// }
298    /// ```
299    #[inline]
300    pub fn new(arr: GenericArray<T, N>) -> NumericArray<T, N> {
301        NumericArray(arr)
302    }
303
304    /// Creates a new array filled with a single value.
305    ///
306    /// Example:
307    ///
308    /// ```ignore
309    /// let a = NumericArray::new(arr![5, 5, 5, 5]);
310    /// let b = NumericArray::splat(5);
311    ///
312    /// assert_eq!(a, b);
313    /// ```
314    #[inline]
315    pub fn splat(t: T) -> NumericArray<T, N>
316    where
317        T: Clone,
318    {
319        NumericArray(GenericArray::generate(|_| t.clone()))
320    }
321
322    /// Convert all elements of the `NumericArray` to another `NumericArray` using `From`
323    pub fn convert<U: From<T>>(self) -> NumericArray<U, N> {
324        self.0.map(From::from).into()
325    }
326
327    /// Consumes self and returns the internal `GenericArray` instance
328    #[inline]
329    pub fn into_array(self) -> GenericArray<T, N> {
330        self.0
331    }
332
333    /// Get reference to underlying `GenericArray` instance.
334    #[inline]
335    pub fn as_array(&self) -> &GenericArray<T, N> {
336        &self.0
337    }
338
339    /// Get mutable reference to underlying `GenericArray` instance.
340    #[inline(always)]
341    pub fn as_mut_array(&mut self) -> &mut GenericArray<T, N> {
342        &mut self.0
343    }
344
345    /// Extracts a slice containing the entire array.
346    #[inline(always)]
347    pub fn as_slice(&self) -> &[T] {
348        &self.0
349    }
350
351    /// Extracts a mutable slice containing the entire array.
352    #[inline(always)]
353    pub fn as_mut_slice(&mut self) -> &mut [T] {
354        &mut self.0
355    }
356
357    /// Converts slice to a numeric array reference with inferred length;
358    ///
359    /// Length of the slice must be equal to the length of the array.
360    #[inline(always)]
361    pub fn from_slice(slice: &[T]) -> &NumericArray<T, N> {
362        slice.into()
363    }
364
365    /// Converts mutable slice to a mutable numeric array reference
366    ///
367    /// Length of the slice must be equal to the length of the array.
368    #[inline(always)]
369    pub fn from_mut_slice(slice: &mut [T]) -> &mut NumericArray<T, N> {
370        slice.into()
371    }
372}
373
374use core::ops::Sub;
375use typenum::{bit::B1 as True, Diff, IsGreaterOrEqual};
376
377impl<T, N: ArrayLength> NumericArray<T, N> {
378    /// Offset the numeric array and cast it into a shorter array
379    #[inline(always)]
380    pub fn offset<V: ArrayLength, O: ArrayLength>(&self) -> &NumericArray<T, V>
381    where
382        N: Sub<O>,
383        Diff<N, O>: IsGreaterOrEqual<V, Output = True>,
384    {
385        unsafe { &*((self as *const _ as *const T).add(O::USIZE) as *const NumericArray<T, V>) }
386    }
387
388    /// Offset the numeric array and cast it into a shorter array
389    #[inline(always)]
390    pub fn offset_mut<V: ArrayLength, O: ArrayLength>(&mut self) -> &mut NumericArray<T, V>
391    where
392        N: Sub<O>,
393        Diff<N, O>: IsGreaterOrEqual<V, Output = True>,
394    {
395        unsafe { &mut *((self as *mut _ as *mut T).add(O::USIZE) as *mut NumericArray<T, V>) }
396    }
397}
398
399impl<'a, T, N: ArrayLength> From<&'a [T]> for &'a NumericArray<T, N> {
400    /// Converts slice to a numeric array reference with inferred length;
401    ///
402    /// Length of the slice must be equal to the length of the array.
403    #[inline(always)]
404    fn from(slice: &[T]) -> &NumericArray<T, N> {
405        debug_assert_eq!(slice.len(), N::to_usize());
406
407        unsafe { &*(slice.as_ptr() as *const NumericArray<T, N>) }
408    }
409}
410
411impl<'a, T, N: ArrayLength> From<&'a mut [T]> for &'a mut NumericArray<T, N> {
412    /// Converts mutable slice to a mutable numeric array reference
413    ///
414    /// Length of the slice must be equal to the length of the array.
415    #[inline(always)]
416    fn from(slice: &mut [T]) -> &mut NumericArray<T, N> {
417        debug_assert_eq!(slice.len(), N::to_usize());
418
419        unsafe { &mut *(slice.as_mut_ptr() as *mut NumericArray<T, N>) }
420    }
421}
422
423impl<T, N: ArrayLength> AsRef<[T]> for NumericArray<T, N> {
424    #[inline(always)]
425    fn as_ref(&self) -> &[T] {
426        self
427    }
428}
429
430impl<T, N: ArrayLength> Borrow<[T]> for NumericArray<T, N> {
431    #[inline(always)]
432    fn borrow(&self) -> &[T] {
433        self
434    }
435}
436
437impl<T, N: ArrayLength> AsMut<[T]> for NumericArray<T, N> {
438    #[inline(always)]
439    fn as_mut(&mut self) -> &mut [T] {
440        self
441    }
442}
443
444impl<T, N: ArrayLength> BorrowMut<[T]> for NumericArray<T, N> {
445    fn borrow_mut(&mut self) -> &mut [T] {
446        self
447    }
448}
449
450impl<T, N: ArrayLength> Index<usize> for NumericArray<T, N> {
451    type Output = T;
452
453    #[inline(always)]
454    fn index(&self, index: usize) -> &T {
455        self.0.index(index)
456    }
457}
458
459impl<T, N: ArrayLength> IndexMut<usize> for NumericArray<T, N> {
460    #[inline(always)]
461    fn index_mut(&mut self, index: usize) -> &mut T {
462        self.0.index_mut(index)
463    }
464}
465
466impl<T, N: ArrayLength> Index<Range<usize>> for NumericArray<T, N> {
467    type Output = [T];
468
469    #[inline(always)]
470    fn index(&self, index: Range<usize>) -> &[T] {
471        self.0.index(index)
472    }
473}
474
475impl<T, N: ArrayLength> IndexMut<Range<usize>> for NumericArray<T, N> {
476    #[inline(always)]
477    fn index_mut(&mut self, index: Range<usize>) -> &mut [T] {
478        self.0.index_mut(index)
479    }
480}
481
482impl<T, N: ArrayLength> Index<RangeTo<usize>> for NumericArray<T, N> {
483    type Output = [T];
484
485    #[inline(always)]
486    fn index(&self, index: RangeTo<usize>) -> &[T] {
487        self.0.index(index)
488    }
489}
490
491impl<T, N: ArrayLength> IndexMut<RangeTo<usize>> for NumericArray<T, N> {
492    #[inline(always)]
493    fn index_mut(&mut self, index: RangeTo<usize>) -> &mut [T] {
494        self.0.index_mut(index)
495    }
496}
497
498impl<T, N: ArrayLength> Index<RangeFrom<usize>> for NumericArray<T, N> {
499    type Output = [T];
500
501    #[inline(always)]
502    fn index(&self, index: RangeFrom<usize>) -> &[T] {
503        self.0.index(index)
504    }
505}
506
507impl<T, N: ArrayLength> IndexMut<RangeFrom<usize>> for NumericArray<T, N> {
508    #[inline(always)]
509    fn index_mut(&mut self, index: RangeFrom<usize>) -> &mut [T] {
510        self.0.index_mut(index)
511    }
512}
513
514impl<T, N: ArrayLength> Index<RangeFull> for NumericArray<T, N> {
515    type Output = [T];
516
517    #[inline(always)]
518    fn index(&self, _index: RangeFull) -> &[T] {
519        self
520    }
521}
522
523impl<T, N: ArrayLength> IndexMut<RangeFull> for NumericArray<T, N> {
524    #[inline(always)]
525    fn index_mut(&mut self, _index: RangeFull) -> &mut [T] {
526        self
527    }
528}
529
530impl<'a, T, N: ArrayLength> IntoIterator for &'a NumericArray<T, N> {
531    type Item = &'a T;
532    type IntoIter = slice::Iter<'a, T>;
533
534    #[inline(always)]
535    fn into_iter(self) -> Self::IntoIter {
536        self.iter()
537    }
538}
539
540impl<'a, T, N: ArrayLength> IntoIterator for &'a mut NumericArray<T, N> {
541    type Item = &'a mut T;
542    type IntoIter = slice::IterMut<'a, T>;
543
544    #[inline(always)]
545    fn into_iter(self) -> Self::IntoIter {
546        self.iter_mut()
547    }
548}
549
550impl<T, N: ArrayLength> IntoIterator for NumericArray<T, N> {
551    type Item = T;
552    type IntoIter = GenericArrayIter<T, N>;
553
554    #[inline(always)]
555    fn into_iter(self) -> Self::IntoIter {
556        self.0.into_iter()
557    }
558}
559
560impl<T, N: ArrayLength> FromIterator<T> for NumericArray<T, N> {
561    #[inline(always)]
562    fn from_iter<I>(iter: I) -> Self
563    where
564        I: IntoIterator<Item = T>,
565    {
566        NumericArray(GenericArray::from_iter(iter))
567    }
568}
569
570impl<T, N: ArrayLength> Default for NumericArray<T, N>
571where
572    T: Default,
573{
574    #[inline(always)]
575    fn default() -> Self {
576        NumericArray(GenericArray::default())
577    }
578}
579
580#[cfg(test)]
581pub mod tests {
582    use num_traits::float::FloatCore;
583
584    // This stops the compiler from optimizing based on known data, only data types.
585    #[inline(never)]
586    pub fn black_box<T>(val: T) -> T {
587        use core::{mem, ptr};
588
589        let ret = unsafe { ptr::read_volatile(&val) };
590        mem::forget(val);
591        ret
592    }
593
594    #[test]
595    fn test_ops() {
596        let a = black_box(narr![1, 3, 5, 7]);
597        let b = black_box(narr![2, 4, 6, 8]);
598
599        let c = a + b;
600        let d = c * nconstant!(black_box(5));
601        let e = d << nconstant!(1_usize);
602
603        assert_eq!(e, narr![30, 70, 110, 150])
604    }
605
606    #[test]
607    fn test_constants() {
608        let a = black_box(narr![1, 3, 5, 7]);
609        let b = black_box(narr![2, 4, 6, 8]);
610
611        let c = a + b * nconstant!(2);
612
613        assert_eq!(c, narr![5, 11, 17, 23]);
614    }
615
616    #[test]
617    fn test_floats() {
618        let a = black_box(narr![1.0f32, 3.0, 5.0, 7.0]);
619        let b = black_box(narr![2.0f32, 4.0, 6.0, 8.0]);
620
621        let c = a + b;
622
623        black_box(c);
624    }
625
626    #[test]
627    fn test_other() {
628        use num_traits::Saturating;
629
630        let a = black_box(narr![1, 3, 5, 7]);
631        let b = black_box(narr![2, 4, 6, 8]);
632
633        let c = a.saturating_add(b);
634
635        black_box(c);
636    }
637
638    #[cfg(feature = "std")]
639    #[test]
640    fn test_atan2() {
641        use num_traits::Float;
642
643        let a = black_box(narr![1.0f32, 2.0, 3.0, 4.0]);
644        let b = black_box(narr![2.0f32, 3.0, 4.0, 5.0]);
645
646        let c = a.atan2(b);
647
648        assert_eq!(c, narr![0.4636476, 0.5880026, 0.6435011, 0.67474097]);
649    }
650
651    #[test]
652    fn test_classify() {
653        use core::num::FpCategory;
654
655        let nan = f32::nan();
656        let infinity = f32::infinity();
657
658        let any_nan = black_box(narr![1.0, 2.0, nan, 0.0]);
659        let any_infinite = black_box(narr![1.0, infinity, 2.0, 3.0]);
660        let any_mixed = black_box(narr![1.0, infinity, nan, 0.0]);
661        let all_normal = black_box(narr![1.0, 2.0, 3.0, 4.0]);
662        let all_zero = black_box(narr![0.0, 0.0, 0.0, 0.0]);
663
664        let non_zero = black_box(narr![0.0f32, 1.0, 0.0, 0.0]);
665
666        assert_eq!(any_nan.classify(), FpCategory::Nan);
667        assert_eq!(any_mixed.classify(), FpCategory::Nan);
668        assert_eq!(any_infinite.classify(), FpCategory::Infinite);
669        assert_eq!(all_normal.classify(), FpCategory::Normal);
670        assert_eq!(all_zero.classify(), FpCategory::Zero);
671
672        assert_eq!(non_zero.classify(), FpCategory::Normal);
673
674        assert!(!any_nan.is_infinite());
675        assert!(any_mixed.is_infinite());
676        assert!(any_nan.is_nan());
677        assert!(any_mixed.is_nan());
678        assert!(!any_infinite.is_nan());
679    }
680
681    #[cfg(feature = "std")]
682    #[test]
683    fn test_tanh() {
684        use num_traits::Float;
685
686        let a = black_box(narr![1.0f32, 2.0, 3.0, 4.0]);
687
688        black_box(a.tanh());
689    }
690
691    #[cfg(feature = "std")]
692    #[test]
693    pub fn test_madd() {
694        use num_traits::Float;
695
696        let a = black_box(narr![1.0f32, 2.0, 3.0, 4.0]);
697        let b = black_box(narr![5.0f32, 6.0, 7.0, 8.0]);
698        let c = black_box(narr![9.0f32, 1.0, 2.0, 3.0]);
699
700        let d = a.mul_add(b, c);
701
702        assert_eq!(d, narr![14.0, 13.0, 23.0, 35.0]);
703    }
704
705    #[test]
706    #[no_mangle]
707    pub fn test_select() {
708        use crate::simd::Select;
709
710        let mask = black_box(narr![true, false, false, true]);
711
712        let a = black_box(narr![1, 2, 3, 4]);
713        let b = black_box(narr![5, 6, 7, 8]);
714
715        // Compiles to vblendvps
716        let selected = mask.select(a, b);
717
718        assert_eq!(selected, narr![1, 6, 7, 4]);
719    }
720}