dsplib 1.1.0
C++ DSP library for MATLAB-like coding
Loading...
Searching...
No Matches
array.h
1#pragma once
2
3#include <type_traits>
4#include <vector>
5#include <cassert>
6#include <cmath>
7
8#include <dsplib/types.h>
9#include <dsplib/slice.h>
10#include <dsplib/indexing.h>
11#include <dsplib/assert.h>
12#include <dsplib/span.h>
13#include <dsplib/traits.h>
14
15namespace dsplib {
16
23template<typename T>
25{
26 static_assert(support_type_for_array<T>(), "type is not supported");
27
28public:
29 base_array() noexcept = default;
30
31 explicit base_array(int n)
32 : _vec(n, 0) {
33 }
34
35 base_array(const std::vector<T>& v)
36 : _vec(v) {
37 }
38
39 template<typename T2, std::enable_if_t<is_array_convertible<T2, T>(), bool> = true>
40 base_array(const std::vector<T2>& v)
41 : base_array(make_span(v)) {
42 }
43
44 base_array(std::vector<T>&& v) noexcept
45 : _vec(std::move(v)) {
46 }
47
49 : _vec(v._vec) {
50 }
51
52 template<class T2, std::enable_if_t<is_array_convertible<T2, T>(), bool> = true>
53 base_array(const base_array<T2>& v) {
54 _vec.assign(v.begin(), v.end());
55 }
56
57 base_array(base_array<T>&& v) noexcept
58 : _vec(std::move(v._vec)) {
59 }
60
61 base_array(const std::initializer_list<T>& list)
62 : _vec(list) {
63 }
64
65 base_array(const span_t<T>& v)
66 : _vec(v.data(), v.data() + v.size()) {
67 }
68
70 : _vec(v.data(), v.data() + v.size()) {
71 }
72
73 base_array(const slice_t<T>& rhs)
74 : base_array(rhs.size()) {
75 this->slice(0, indexing::end) = rhs;
76 }
77
78 base_array(const mut_slice_t<T>& rhs)
79 : base_array(slice_t<T>(rhs)) {
80 }
81
82 template<typename T2, std::enable_if_t<is_array_convertible<T2, T>(), bool> = true>
83 base_array(const span_t<T2>& v) {
84 _vec.assign(v.begin(), v.end());
85 }
86
87 template<typename T2, std::enable_if_t<is_array_convertible<T2, T>(), bool> = true>
88 base_array(const mut_span_t<T2>& v) {
89 _vec.assign(v.begin(), v.end());
90 }
91
92 explicit base_array(const T* ptr, size_t size)
93 : base_array(make_span(ptr, size)) {
94 }
95
96 //--------------------------------------------------------------------
97 base_array<T>& operator=(const base_array<T>& rhs) {
98 if (this == &rhs) {
99 return *this;
100 }
101 _vec = rhs._vec;
102 return *this;
103 }
104
105 base_array<T>& operator=(base_array<T>&& rhs) noexcept {
106 if (this == &rhs) {
107 return *this;
108 }
109 _vec.swap(rhs._vec);
110 return *this;
111 }
112
113 //--------------------------------------------------------------------
114 template<typename Index, std::enable_if_t<std::is_integral_v<Index>, bool> = true>
115 const T& operator[](Index i) const noexcept {
116 const size_t n = _vec.size();
117 if constexpr (std::is_signed_v<Index>) {
118 const size_t idx = (i >= 0) ? static_cast<size_t>(i) : (n + i);
119 DSPLIB_ASSUME(idx < n);
120 return _vec[idx];
121 } else {
122 DSPLIB_ASSUME(i < n);
123 return _vec[i];
124 }
125 }
126
127 template<typename Index, std::enable_if_t<std::is_integral_v<Index>, bool> = true>
128 T& operator[](Index i) noexcept {
129 return const_cast<T&>(static_cast<const base_array<T>&>(*this)[i]);
130 }
131
132 //--------------------------------------------------------------------
133 base_array<T> operator[](const std::vector<bool>& idxs) const {
134 DSPLIB_ASSERT(idxs.size() == _vec.size(), "Array sizes must be equal");
135 std::vector<T> res;
136 res.reserve(_vec.size());
137 for (size_t i = 0; i < idxs.size(); ++i) {
138 if (idxs[i]) {
139 res.push_back(_vec[i]);
140 }
141 }
142 return res;
143 }
144
145 base_array<T> operator[](const std::vector<int>& idxs) const {
146 const size_t max_i = *std::max_element(idxs.begin(), idxs.end());
147 DSPLIB_ASSERT(max_i < _vec.size(), "Index must not exceed the size of the vector");
148 std::vector<T> res(idxs.size());
149 for (size_t i = 0; i < idxs.size(); ++i) {
150 res[i] = _vec[idxs[i]];
151 }
152 return res;
153 }
154
155 base_array<T> operator[](const base_array<int>& idxs) const {
156 return (*this)[idxs.to_vec()];
157 }
158
159 //--------------------------------------------------------------------
160 //compare scalar
161 std::vector<bool> operator>(T val) const noexcept {
162 std::vector<bool> res(_vec.size());
163 for (size_t i = 0; i < _vec.size(); ++i) {
164 res[i] = (_vec[i] > val);
165 }
166 return res;
167 }
168
169 std::vector<bool> operator<(T val) const noexcept {
170 std::vector<bool> res(_vec.size());
171 for (size_t i = 0; i < _vec.size(); ++i) {
172 res[i] = (_vec[i] < val);
173 }
174 return res;
175 }
176
177 std::vector<bool> operator==(T val) const noexcept {
178 std::vector<bool> res(_vec.size());
179 for (size_t i = 0; i < _vec.size(); ++i) {
180 //TODO: add tolerance for compare?
181 res[i] = (_vec[i] == val);
182 }
183 return res;
184 }
185
186 std::vector<bool> operator!=(T val) const noexcept {
187 auto r = (*this == val);
188 r.flip();
189 return r;
190 }
191
192 //--------------------------------------------------------------------
193 //compare vector
194 std::vector<bool> operator>(const base_array<T>& rhs) const noexcept {
195 std::vector<bool> res(_vec.size());
196 for (size_t i = 0; i < _vec.size(); ++i) {
197 res[i] = (_vec[i] > rhs._vec[i]);
198 }
199 return res;
200 }
201
202 std::vector<bool> operator<(const base_array<T>& rhs) const noexcept {
203 std::vector<bool> res(_vec.size());
204 for (size_t i = 0; i < _vec.size(); ++i) {
205 res[i] = (_vec[i] < rhs._vec[i]);
206 }
207 return res;
208 }
209
210 std::vector<bool> operator==(const base_array<T>& rhs) const noexcept {
211 std::vector<bool> res(_vec.size());
212 for (size_t i = 0; i < _vec.size(); ++i) {
213 res[i] = (_vec[i] == rhs._vec[i]);
214 }
215 return res;
216 }
217
218 std::vector<bool> operator!=(const base_array<T>& rhs) const noexcept {
219 auto r = (*this == rhs);
220 r.flip();
221 return r;
222 }
223
224 //--------------------------------------------------------------------
225 mut_slice_t<T> slice(int i1, int i2, int m) {
226 return mut_slice_t<T>::make_slice(_vec.data(), _vec.size(), i1, i2, m);
227 }
228
229 slice_t<T> slice(int i1, int i2, int m) const {
230 return slice_t<T>::make_slice(_vec.data(), _vec.size(), i1, i2, m);
231 }
232
233 //TODO: add slice(end, first, -1) like x[::-1] numpy
234 mut_slice_t<T> slice(int i1, indexing::end_t, int m) {
235 return this->slice(i1, size(), m);
236 }
237
238 slice_t<T> slice(int i1, indexing::end_t, int m) const {
239 return this->slice(i1, size(), m);
240 }
241
242 //use span for stride=1
243 mut_span_t<T> slice(int i1, int i2) {
244 i1 = (i1 >= 0) ? i1 : (size() + i1);
245 i2 = (i2 >= 0) ? i2 : (size() + i2);
246 DSPLIB_ASSERT((i2 > i1) && (i1 >= 0) && (i2 <= size()), "Invalid slice range");
247 return mut_span_t<T>(_vec.data() + i1, (i2 - i1));
248 }
249
250 mut_span_t<T> slice(int i1, indexing::end_t) {
251 return this->slice(i1, size());
252 }
253
254 span_t<T> slice(int i1, int i2) const {
255 i1 = (i1 >= 0) ? i1 : (size() + i1);
256 i2 = (i2 >= 0) ? i2 : (size() + i2);
257 DSPLIB_ASSERT((i2 > i1) && (i1 >= 0) && (i2 <= size()), "Invalid slice range");
258 return span_t<T>(_vec.data() + i1, (i2 - i1));
259 }
260
261 span_t<T> slice(int i1, indexing::end_t) const {
262 return this->slice(i1, size());
263 }
264
265 //--------------------------------------------------------------------
266 using iterator = typename std::vector<T>::iterator;
267 using const_iterator = typename std::vector<T>::const_iterator;
268
269 iterator begin() noexcept {
270 return _vec.begin();
271 }
272
273 iterator end() noexcept {
274 return _vec.end();
275 }
276
277 const_iterator begin() const noexcept {
278 return _vec.begin();
279 }
280
281 const_iterator end() const noexcept {
282 return _vec.end();
283 }
284
285 T* data() noexcept {
286 return _vec.data();
287 }
288
289 const T* data() const noexcept {
290 return _vec.data();
291 }
292
293 [[nodiscard]] int size() const noexcept {
294 return int(_vec.size());
295 }
296
297 [[nodiscard]] bool empty() const noexcept {
298 return _vec.empty();
299 }
300
301 //--------------------------------------------------------------------
302 const base_array<T>& operator+() const noexcept {
303 return *this;
304 }
305
306 base_array<T> operator-() const noexcept {
307 base_array<T> r{_vec};
308 for (size_t i = 0; i < r.size(); ++i) {
309 r[i] = -r[i];
310 }
311 return r;
312 }
313
314 //--------------------------------------------------------------------
315 template<class T2>
316 base_array<T>& operator+=(const T2& rhs) noexcept {
317 make_span(_vec) += rhs;
318 return *this;
319 }
320
321 template<class T2>
322 base_array<T>& operator-=(const T2& rhs) noexcept {
323 make_span(_vec) -= rhs;
324 return *this;
325 }
326
327 template<class T2>
328 base_array<T>& operator*=(const T2& rhs) noexcept {
329 make_span(_vec) *= rhs;
330 return *this;
331 }
332
333 template<class T2>
334 base_array<T>& operator/=(const T2& rhs) {
335 make_span(_vec) /= rhs;
336 return *this;
337 }
338
339 //--------------------------------------------------------------------
340 //TODO: add lvalue + rvalue, rvalue + lvalue
341 template<class T2>
342 auto operator+(const T2& rhs) const {
343 return make_span(_vec) + rhs;
344 }
345
346 template<class T2>
347 auto operator-(const T2& rhs) const {
348 return make_span(_vec) - rhs;
349 }
350
351 template<class T2>
352 auto operator*(const T2& rhs) const {
353 return make_span(_vec) * rhs;
354 }
355
356 template<class T2>
357 auto operator/(const T2& rhs) const {
358 return make_span(_vec) / rhs;
359 }
360
361 //concatenate syntax
362 template<class T2, class R = ResultType<T, T2>>
363 base_array& operator|=(const base_array<T2>& rhs) {
364 static_assert(std::is_same_v<T, R>, "not supported array cast");
365 _vec.insert(_vec.end(), rhs.begin(), rhs.end());
366 return *this;
367 }
368
369 template<class T2, class R = ResultType<T, T2>>
370 base_array<R> operator|(const base_array<T2>& rhs) const {
371 auto r = this->cast<R>();
372 r |= rhs;
373 return r;
374 }
375
376 template<typename R>
377 [[nodiscard]] std::vector<R> to_vec() const noexcept {
378 if constexpr (std::is_same_v<T, R>) {
379 return _vec;
380 } else {
381 static_assert(std::is_convertible_v<T, R>, "type must be convertible");
382 return std::vector<R>(_vec.begin(), _vec.end());
383 }
384 }
385
386 [[nodiscard]] const std::vector<T>& to_vec() const noexcept {
387 return _vec;
388 }
389
390 template<typename R>
391 [[nodiscard]] auto cast() const noexcept {
392 if constexpr (std::is_same_v<T, R>) {
393 return *this;
394 } else {
395 static_assert(std::is_convertible_v<T, R>, "type must be convertible");
396 return base_array<R>(this->to_vec<R>());
397 }
398 }
399
400 //apply per element function
401 template<class Fn>
402 auto apply(Fn func) const {
403 using R = decltype(func(T()));
404 if constexpr (std::is_same_v<T, R>) {
405 base_array<T> out(*this);
406 for (T& v : out) {
407 v = func(v);
408 }
409 return out;
410 } else {
411 base_array<R> out(size());
412 R* pout = out.data();
413 for (const T& v : _vec) {
414 *pout++ = func(v);
415 }
416 return out;
417 }
418 }
419
420 friend std::ostream& operator<<(std::ostream& os, const base_array& x) {
421 return x._print(os);
422 }
423
424protected:
425 std::ostream& _print(std::ostream& os) const;
426 std::vector<T> _vec;
427};
428
429//--------------------------------------------------------------------------------
430//left oriented scalar * array
431template<class T, class Scalar, class = enable_scalar_t<Scalar>>
432auto operator+(const Scalar& lhs, const base_array<T>& rhs) {
433 using R = ResultType<T, Scalar>;
434 static_assert(std::is_convertible_v<Scalar, R>, "convertable type error");
435 return rhs + lhs;
436}
437
438template<class T, class S, class = enable_scalar_t<S>>
439auto operator-(const S& lhs, const base_array<T>& rhs) {
440 using R = ResultType<T, S>;
441 static_assert(std::is_convertible_v<S, R>, "convertable type error");
442 return (-rhs) + lhs;
443}
444
445template<class T, class S, class = enable_scalar_t<S>>
446auto operator*(const S& lhs, const base_array<T>& rhs) {
447 using R = ResultType<T, S>;
448 static_assert(std::is_convertible_v<S, R>, "convertable type error");
449 return rhs * lhs;
450}
451
452template<class T, class S, class = enable_scalar_t<S>>
453auto operator/(const S& lhs, const base_array<T>& rhs) {
454 using R = ResultType<T, S>;
455 static_assert(std::is_convertible_v<S, R>, "convertable type error");
456 auto r = base_array<R>(rhs.size());
457 const auto d = R(lhs);
458 for (size_t i = 0; i < r.size(); ++i) {
459 r[i] = d / rhs[i];
460 }
461 return r;
462}
463
464//----------------------------------------------------------------------------------------
465inline base_array<cmplx_t> operator*(const base_array<real_t>& lhs, const std::complex<double>& rhs) {
466 //TODO: optimization for rhs == 1i
467 return lhs * cmplx_t(rhs);
468}
469
470inline base_array<cmplx_t> operator*(const std::complex<double>& lhs, const base_array<real_t>& rhs) {
471 return rhs * cmplx_t(lhs);
472}
473
474//----------------------------------------------------------------------------------------
475using arr_real = base_array<real_t>;
476using arr_cmplx = base_array<cmplx_t>;
477using arr_int = base_array<int>;
478
479} // namespace dsplib
base dsplib array type
Definition array.h:25
Mutable slice object.
Definition slice.h:100
Definition span.h:27
Non-mutable slice object.
Definition slice.h:27
Definition span.h:295
Definition indexing.h:7