mu
vector.h
Go to the documentation of this file.
1 
6 #ifndef MU_VECTOR_H_
7 #define MU_VECTOR_H_
8 
9 #include <algorithm>
10 #include <array>
11 #include <cassert>
12 #include <cmath>
13 #include <initializer_list>
14 #include <numeric>
15 #include <ostream>
16 #include <type_traits>
17 #include <utility>
18 
19 #include "mu/typetraits.h"
20 #include "mu/utility.h"
21 
22 namespace mu {
23 
31 template <std::size_t X, std::size_t Y, typename Z>
32 class Matrix;
33 
47 template <std::size_t N, typename T>
48 class Vector {
49  static_assert(N != 0, "Vector dimension cannot be zero");
50  static_assert(std::is_arithmetic<T>::value,
51  "Vector type T must be an arithmetic type");
52 
53  public:
54  /* value and size type from the underlying container */
55  using value_type = typename std::array<T, N>::value_type;
56  using size_type = typename std::array<T, N>::size_type;
57  /* use iterators from the underlying container */
58  using iterator = typename std::array<T, N>::iterator;
59  using const_iterator = typename std::array<T, N>::const_iterator;
60 
67  constexpr Vector() = default;
68 
80  template <typename... TArgs,
81  std::enable_if_t<
82  sizeof...(TArgs) == N &&
83  (mu::conjunction<std::is_same<
84  T, std::remove_reference_t<TArgs>>::value...>::value),
85  int> = 0>
86  // NOLINTNEXTLINE(runtime/explicit) implicit to make copy-init. work
87  Vector(TArgs &&... args) : data_{std::forward<TArgs>(args)...} {}
88 
101  template <std::size_t Nn, class U>
102  // NOLINTNEXTLINE(runtime/explicit) implicit conversion is intentional
103  Vector(const Vector<Nn, U> &v) {
104  static_assert(N == Nn, "Vector size mismatch");
105  std::transform(v.begin(), v.end(), begin(), [](U data) { return data; });
106  }
107 
115  // NOLINTNEXTLINE(runtime/explicit) implicit to make copy-init. work
116  Vector(const std::array<T, N> &a) : data_{a} {}
117 
128  template <typename U = T,
129  std::enable_if_t<std::is_arithmetic<U>::value, int> = 0>
130  // NOLINTNEXTLINE(runtime/explicit) implicit to make copy-init. work
131  Vector(const std::array<U, N> &a) {
132  std::transform(a.begin(), a.end(), begin(), [](U data) { return data; });
133  }
134 
145  template <typename U = T,
146  std::enable_if_t<std::is_arithmetic<U>::value, int> = 0>
147  // NOLINTNEXTLINE(runtime/explicit) implicit to make copy-init. work
148  Vector(const U &value) {
149  data_.fill(value);
150  }
151 
156  ~Vector() = default;
157 
165  Vector(const Vector &other) = default;
166 
174  Vector(Vector &&other) noexcept = default;
175 
184  Vector &operator=(const Vector &other) = default;
185 
194  Vector &operator=(Vector &&other) noexcept = default;
195 
206  T &operator[](size_type idx) noexcept { return data_[idx]; }
207 
218  const T &operator[](size_type idx) const noexcept { return data_[idx]; }
219 
229  T &at(size_type idx) { return data_.at(idx); }
230 
240  const T &at(size_type idx) const { return data_.at(idx); }
241 
249  constexpr size_type size() const noexcept { return N; }
250 
258  iterator begin() noexcept { return data_.begin(); }
259 
267  const_iterator begin() const noexcept { return data_.begin(); }
268 
277  iterator end() noexcept { return data_.end(); }
278 
287  const_iterator end() const noexcept { return data_.end(); }
288 
296  T min() const {
297  T ret(data_[0]);
298  for (std::size_t i = 1; i < N; i++) {
299  ret = mu::min(ret, data_[i]);
300  }
301  return ret;
302  }
303 
311  T max() const {
312  T ret(data_[0]);
313  for (std::size_t i = 1; i < N; i++) {
314  ret = mu::max(ret, data_[i]);
315  }
316  return ret;
317  }
318 
326  T sum() const {
327  T ret{};
328  for (const auto &item : data_) {
329  ret += item;
330  }
331  return ret;
332  }
333 
348  template <typename U = T>
349  U mean() const {
350  return U(sum()) / N;
351  }
352 
379  template <typename U = void, std::size_t N2, typename T2>
380  std::conditional_t<std::is_same<U, void>::value, T, U> dot(
381  const Vector<N2, T2> &rhs) const {
382  static_assert(N == N2, "Vector size mismatch");
383  using U_ = std::conditional_t<!std::is_same<T, T2>::value, U, T>;
384  static_assert(!std::is_same<U_, void>::value,
385  "Vector types are different. please specify the return "
386  "type. e.g. \"vec1.dot<float>(vec2);\"");
387  U_ ret{};
388  for (std::size_t i = 0; i < N; i++) {
389  ret += data_[i] * rhs[i];
390  }
391  return ret;
392  }
393 
426  template <typename U = void, std::size_t N2, std::size_t M2, typename T2>
427  std::conditional_t<std::is_same<U, void>::value, Vector<M2, T>, Vector<M2, U>>
428  dot(const Matrix<N2, M2, T2> &rhs) const {
429  static_assert(N == N2,
430  "Vector-Matrix dimension mismatch. Vector size must be equal "
431  "to the first dimension of the matrix");
432  using U_ = std::conditional_t<!std::is_same<T, T2>::value, U, T>;
433  static_assert(
434  !std::is_same<U_, void>::value,
435  "Vector and Matrix types are different. please specify the return "
436  "type. e.g. \"vec.dot<float>(mat);\"");
437  Vector<M2, U_> ret;
438  for (std::size_t i = 0; i < M2; i++) {
439  U_ sum{0};
440  for (std::size_t k = 0; k < N; k++) {
441  sum += (data_[k] * rhs[k][i]);
442  }
443  ret[i] = sum;
444  }
445  return ret;
446  }
447 
458  template <class U = T>
459  U std() const {
460  U sum{0};
461  U m = mean<U>();
462  for (const auto &item : data_) {
463  sum += mu::pow(item - m, 2);
464  }
465  return U(mu::sqrt(sum / N));
466  }
467 
480  template <class U = T>
481  U length() const {
482  return U(mu::sqrt(dot(*this)));
483  }
484 
495  void normalize() { *this /= length(); }
496 
506  Vector<N, T> ret(*this);
507  ret.normalize();
508  return ret;
509  }
510 
517  void flip() { mu::reverse(begin(), end()); }
518 
528  Vector<N, T> ret(*this);
529  ret.flip();
530  return ret;
531  }
532 
539  void sort() { mu::sort(begin(), end()); }
540 
551  template <typename Compare>
552  void sort(const Compare &comp) {
553  mu::sort(begin(), end(), comp);
554  }
555 
564  Vector<N, T> ret(*this);
565  ret.sort();
566  return ret;
567  }
568 
578  template <typename Compare>
579  Vector<N, T> sorted(const Compare &comp) const {
580  Vector<N, T> ret(*this);
581  ret.sort(comp);
582  return ret;
583  }
584 
585  /********************************* I/O ***********************************/
586 
598  template <std::size_t Nn, class U>
599  friend std::ostream &operator<<(std::ostream &os, const Vector<Nn, U> &v);
600 
601  /*************************** vector <> vector ****************************/
602 
616  template <typename U = T>
617  bool operator==(const Vector<N, U> &rhs) const {
618  for (std::size_t i = 0; i < N; i++) {
619  if (!mu::TypeTraits<T>::equals(data_[i], rhs[i]) ||
620  !mu::TypeTraits<U>::equals(data_[i], rhs[i])) {
621  return false;
622  }
623  }
624  return true;
625  }
626 
633  template <typename U = T>
634  bool operator!=(const Vector<N, U> &rhs) const {
635  return !operator==(rhs);
636  }
637 
647  template <typename U = T>
649  for (std::size_t i = 0; i < N; i++) {
650  data_[i] += rhs[i];
651  }
652  return *this;
653  }
654 
664  template <typename U = T>
666  for (std::size_t i = 0; i < N; i++) {
667  data_[i] -= rhs[i];
668  }
669  return *this;
670  }
671 
681  template <typename U = T>
683  for (std::size_t i = 0; i < N; i++) {
684  data_[i] *= rhs[i];
685  }
686  return *this;
687  }
688 
700  template <typename U = T>
702  for (std::size_t i = 0; i < N; i++) {
703  data_[i] /= rhs[i];
704  }
705  return *this;
706  }
707 
708  /*************************** vector <> scalar ****************************/
709 
710  /*
711  * enable_if's are used to check for an arithmetic type at compile time
712  * since it's the only type "family" that is allowed inside a Vector.
713  *
714  * the vector elements and the scalar can be of different types. c++ usual
715  * conversion rules apply.
716  *
717  * placed inside this class because write access to member data is required
718  */
719 
727  template <class TScalar>
728  typename std::enable_if_t<std::is_arithmetic<TScalar>::value, Vector<N, T> &>
729  operator+=(const TScalar &scalar) {
730  for (auto &item : data_) {
731  item += scalar;
732  }
733  return *this;
734  }
735 
744  template <class TScalar>
745  typename std::enable_if_t<std::is_arithmetic<TScalar>::value, Vector<N, T> &>
746  operator-=(const TScalar &scalar) {
747  for (auto &item : data_) {
748  item -= scalar;
749  }
750  return *this;
751  }
752 
760  template <class TScalar>
761  typename std::enable_if_t<std::is_arithmetic<TScalar>::value, Vector<N, T> &>
762  operator*=(const TScalar &scalar) {
763  for (auto &item : data_) {
764  item *= scalar;
765  }
766  return *this;
767  }
768 
779  template <class TScalar>
780  typename std::enable_if_t<std::is_arithmetic<TScalar>::value, Vector<N, T> &>
781  operator/=(const TScalar &scalar) {
782  /* a division by zero of an integral type is undefined in standard c++
783  * however, in the context of this Vector class, it is seen as rather
784  * harmful and can become the source of bugs. debug mode only */
785  if (std::is_integral<TScalar>::value) {
786  assert(scalar != static_cast<TScalar>(0));
787  }
788  for (auto &item : data_) {
789  item /= scalar;
790  }
791  return *this;
792  }
793 
794  /*************************************************************************/
795 
796  protected:
797  std::array<T, N> data_;
798 };
799 
800 /********************************** I/O ************************************/
801 
802 template <std::size_t Nn, class U>
803 std::ostream &operator<<(std::ostream &os, const Vector<Nn, U> &v) {
804  os << "[ ";
805  for (std::size_t i = 0; i < Nn; i++) {
806  os << v.data_[i];
807  if (i < (Nn - 1)) {
808  os << ", ";
809  } else {
810  os << " ";
811  }
812  }
813  os << "]";
814  return os;
815 }
816 
817 /**************************** vector <> vector *****************************/
818 
829 template <std::size_t N, class T, class U = T>
831  const Vector<N, U> &rhs) {
832  return Vector<N, T>(lhs) += rhs;
833 }
834 
845 template <std::size_t N, class T, class U = T>
847  const Vector<N, U> &rhs) {
848  return Vector<N, T>(lhs) -= rhs;
849 }
850 
861 template <std::size_t N, class T, class U = T>
863  const Vector<N, U> &rhs) {
864  return Vector<N, T>(lhs) *= rhs;
865 }
866 
877 template <std::size_t N, class T, class U = T>
879  const Vector<N, U> &rhs) {
880  return Vector<N, T>(lhs) /= rhs;
881 }
882 
883 /**************************** vector <> scalar *****************************/
884 
897 template <std::size_t N, class T, class TScalar>
898 typename std::enable_if_t<std::is_arithmetic<TScalar>::value,
899  Vector<N, T>> inline
900 operator+(const Vector<N, T> &lhs, const TScalar &rhs) {
901  return Vector<N, T>(lhs) += rhs;
902 }
903 
916 template <std::size_t N, class T, class TScalar>
917 typename std::enable_if_t<std::is_arithmetic<TScalar>::value,
918  Vector<N, T>> inline
919 operator+(const TScalar &lhs, const Vector<N, T> &rhs) {
920  return Vector<N, T>(rhs) += lhs;
921 }
922 
933 template <std::size_t N, class T, class TScalar>
934 typename std::enable_if_t<std::is_arithmetic<TScalar>::value,
935  Vector<N, T>> inline
936 operator-(const Vector<N, T> &lhs, const TScalar &rhs) {
937  return Vector<N, T>(lhs) -= rhs;
938 }
939 
952 template <std::size_t N, class T, class TScalar>
953 typename std::enable_if_t<std::is_arithmetic<TScalar>::value,
954  Vector<N, T>> inline
955 operator*(const Vector<N, T> &lhs, const TScalar &rhs) {
956  return Vector<N, T>(lhs) *= rhs;
957 }
958 
971 template <std::size_t N, class T, class TScalar>
972 typename std::enable_if_t<std::is_arithmetic<TScalar>::value,
973  Vector<N, T>> inline
974 operator*(const TScalar &lhs, const Vector<N, T> &rhs) {
975  return Vector<N, T>(rhs) *= lhs;
976 }
977 
988 template <std::size_t N, class T, class TScalar>
989 typename std::enable_if_t<std::is_arithmetic<TScalar>::value,
990  Vector<N, T>> inline
991 operator/(const Vector<N, T> &lhs, const TScalar &rhs) {
992  return Vector<N, T>(lhs) /= rhs;
993 }
994 
995 /************************* convenience functions ***************************/
996 
997 template <std::size_t N, class T>
998 inline T min(const Vector<N, T> &v) {
999  return v.min();
1000 }
1001 
1002 template <std::size_t N, class T>
1003 inline T max(const Vector<N, T> &v) {
1004  return v.max();
1005 }
1006 
1007 template <std::size_t N, class T>
1008 inline T sum(const Vector<N, T> &v) {
1009  return v.sum();
1010 }
1011 
1012 template <class U = void, std::size_t N, typename T>
1013 inline std::conditional_t<std::is_same<U, void>::value, T, U> mean(
1014  const Vector<N, T> &v) {
1015  return v
1016  .template mean<std::conditional_t<std::is_same<U, void>::value, T, U>>();
1017 }
1018 
1019 template <class U = void, std::size_t N1, class T1, std::size_t N2, class T2>
1020 inline std::conditional_t<std::is_same<U, void>::value, T1, U> dot(
1021  const Vector<N1, T1> &lhs, const Vector<N2, T2> &rhs) {
1022  return lhs.template dot<U>(rhs);
1023 }
1024 
1025 template <typename U = void, std::size_t N, typename T, std::size_t N2,
1026  std::size_t M2, typename T2>
1027 std::conditional_t<std::is_same<U, void>::value, Vector<M2, T>, Vector<M2, U>>
1028 dot(const Vector<N, T> &lhs, const Matrix<N2, M2, T2> &rhs) {
1029  return lhs.template dot<U>(rhs);
1030 }
1031 
1039 template <std::size_t N, class T>
1040 // NOLINTNEXTLINE(runtime/references) intentional non-const reference
1041 inline void flip(Vector<N, T> &v) {
1042  v.flip();
1043 }
1044 
1045 template <std::size_t N, class T>
1046 inline Vector<N, T> flipped(const Vector<N, T> &v) {
1047  return v.flipped();
1048 }
1049 
1050 template <std::size_t N, class T>
1051 // NOLINTNEXTLINE(runtime/references) intentional non-const reference
1052 inline void sort(Vector<N, T> &v) {
1053  v.sort();
1054 }
1055 
1056 template <std::size_t N, class T, typename Compare>
1057 // NOLINTNEXTLINE(runtime/references) intentional non-const reference
1058 inline void sort(Vector<N, T> &v, const Compare &compare) {
1059  v.sort(compare);
1060 }
1061 
1062 template <std::size_t N, class T>
1063 inline Vector<N, T> sorted(const Vector<N, T> &v) {
1064  return v.sorted();
1065 }
1066 
1067 template <std::size_t N, class T, typename Compare>
1068 inline Vector<N, T> sorted(const Vector<N, T> &v, const Compare &compare) {
1069  return v.sorted(compare);
1070 }
1071 
1072 template <std::size_t N, typename T = int>
1073 inline Vector<N, T> ones() {
1074  return Vector<N, T>{T{1}};
1075 }
1076 
1077 template <std::size_t N, typename T = int>
1078 inline Vector<N, T> zeros() {
1079  return Vector<N, T>{T{0}};
1080 }
1081 
1082 } // namespace mu
1083 #endif // MU_VECTOR_H_
Matrix< N, M, T > operator-(const Matrix< N, M, T > &lhs, const Matrix< N, M, U > &rhs)
minus operator
Definition: matrix.h:901
std::conditional_t< std::is_same< U, void >::value, Vector< M2, T >, Vector< M2, U > > dot(const Matrix< N2, M2, T2 > &rhs) const
dot product of a vector and a matrix
Definition: vector.h:428
void normalize()
normalizes this vector
Definition: vector.h:495
std::enable_if_t< std::is_arithmetic< TScalar >::value, Vector< N, T > & > operator/=(const TScalar &scalar)
divide every element of this vector by a scalar
Definition: vector.h:781
Matrix< N, M, T > operator/(const Matrix< N, M, T > &lhs, const Matrix< N, M, U > &rhs)
division operator
Definition: matrix.h:935
Vector & operator=(const Vector &other)=default
Copy assignment operator.
Vector(const Vector< Nn, U > &v)
Construct a new Vector from an existing Vector of a different type.
Definition: vector.h:103
Vector< N, T > & operator/=(const Vector< N, U > &rhs)
divison equal operator
Definition: vector.h:701
Matrix< N, M, T > operator*(const Matrix< N, M, T > &lhs, const Matrix< N, M, U > &rhs)
multiplication operator
Definition: matrix.h:918
bool operator==(const Vector< N, U > &rhs) const
equality operator
Definition: vector.h:617
const_iterator begin() const noexcept
returns a const iterator pointing to the first element
Definition: vector.h:267
Vector< N, T > sorted() const
returns a sorted vector
Definition: vector.h:563
A generic vector.
Definition: vector.h:48
constexpr Vector()=default
Construct a new Vector object.
Vector< N, T > normalized()
returns a normalized vector
Definition: vector.h:505
std::conditional_t< std::is_same< U, void >::value, T, U > dot(const Vector< N2, T2 > &rhs) const
dot product of two vectors
Definition: vector.h:380
Vector< N, T > flipped() const
returns a flipped vector
Definition: vector.h:527
Vector(const std::array< T, N > &a)
Construct a new Vector object from an std::array.
Definition: vector.h:116
T max() const
get the max value of the vector
Definition: vector.h:311
Matrix< N, M, T > operator+(const Matrix< N, M, T > &lhs, const Matrix< N, M, U > &rhs)
plus operator
Definition: matrix.h:884
void sort()
sort vector elements in ascending order
Definition: vector.h:539
Vector(TArgs &&... args)
Construct a new Vector object from a number of N values.
Definition: vector.h:87
T & operator[](size_type idx) noexcept
access an element within the vector
Definition: vector.h:206
Vector(const U &value)
Construct a new Vector object from a single value.
Definition: vector.h:148
T min() const
get the min value of the vector
Definition: vector.h:296
~Vector()=default
Destroy the Vector object.
void flip()
flips this vector, i.e. reverses its elements
Definition: vector.h:517
Vector< N, T > & operator-=(const Vector< N, U > &rhs)
minus equal operator
Definition: vector.h:665
U length() const
euclidean vector length
Definition: vector.h:481
bool operator!=(const Vector< N, U > &rhs) const
unequality operator
Definition: vector.h:634
const T & operator[](size_type idx) const noexcept
const access an element within the vector
Definition: vector.h:218
const_iterator end() const noexcept
returns a const iterator pointing to the element following the last element
Definition: vector.h:287
void sort(const Compare &comp)
sort vector elements by providing a condition
Definition: vector.h:552
std::enable_if_t< std::is_arithmetic< TScalar >::value, Vector< N, T > & > operator+=(const TScalar &scalar)
add a scalar to this vector
Definition: vector.h:729
Definition: literals.h:11
A generic matrix.
Definition: matrix.h:36
Vector< N, T > & operator+=(const Vector< N, U > &rhs)
plus equal operator
Definition: vector.h:648
std::enable_if_t< std::is_arithmetic< TScalar >::value, Vector< N, T > & > operator-=(const TScalar &scalar)
subtract a scalar from every element of this vector
Definition: vector.h:746
std::enable_if_t< std::is_arithmetic< TScalar >::value, Vector< N, T > & > operator*=(const TScalar &scalar)
multiply a scalar with this vector
Definition: vector.h:762
U std() const
calculates the standard deviation
Definition: vector.h:459
iterator begin() noexcept
returns an iterator pointing to the first element
Definition: vector.h:258
constexpr size_type size() const noexcept
returns the size of the vector
Definition: vector.h:249
T sum() const
sum up all the elements of the vector
Definition: vector.h:326
Vector< N, T > & operator*=(const Vector< N, U > &rhs)
multiplication equal operator
Definition: vector.h:682
const T & at(size_type idx) const
const access an element within the vector
Definition: vector.h:240
Vector< N, T > sorted(const Compare &comp) const
Definition: vector.h:579
T & at(size_type idx)
access an element within the vector
Definition: vector.h:229
iterator end() noexcept
returns an iterator pointing to the element following the last element
Definition: vector.h:277
Vector(const std::array< U, N > &a)
Construct a new Vector object from an std::array of a different type.
Definition: vector.h:131
U mean() const
mean of all the elements of the vector
Definition: vector.h:349