ampsci
High-precision calculations for one- and two-valence atomic systems
Vector.hpp
1#pragma once
2#include "qip/Methods.hpp"
3#include <algorithm>
4#include <array>
5#include <cassert>
6#include <cmath>
7#include <type_traits>
8#include <utility>
9#include <vector>
10
11namespace qip {
12
13// Uses "no non-const ref" rule. Pass by pointer means modify value
14
15//==============================================================================
16
17//! Merges any number of vectors: {a,b,c},{d,e},{f} -> {a,b,c,d,e,f}.
18template <typename T, typename... Args>
19std::vector<T> merge(std::vector<T> first, const std::vector<T> &second,
20 const Args &...rest) {
21 first.insert(first.end(), second.cbegin(), second.cend());
22 if constexpr (sizeof...(rest) == 0) {
23 return first;
24 } else {
25 return merge(std::move(first), rest...);
26 }
27}
28
29//==============================================================================
30
31/*!
32 @brief Compares two arithmetic vectors element-wise; returns {max_delta, iterator}.
33
34 @details
35 Returns {delta, itr} where delta = max|first - second| (signed as first-second),
36 and itr points to the position in first where the maximum occurred.
37
38 If all compare exactly, max_delta should be zero, and iterator points to begining of list.
39*/
40template <typename T>
41auto compare(const std::vector<T> &first, const std::vector<T> &second) {
42 static_assert(
43 std::is_arithmetic_v<T>,
44 "In compare(std::vector<T>, std::vector<T>), T must be arithmetic");
45 assert(first.size() == second.size()); //?
46
47 auto it1 = first.cbegin();
48 auto it2 = second.cbegin();
49 auto largest_delta = static_cast<T>(0);
50 // auto largest_at = first.cend();
51 auto largest_at = first.cbegin();
52 for (; it1 != first.cend(); ++it1, ++it2) {
53 const auto delta = *it1 - *it2;
54 if (std::abs(delta) > std::abs(largest_delta)) {
55 largest_delta = delta;
56 largest_at = it1;
57 }
58 }
59 return std::make_pair(largest_delta, largest_at);
60}
61
62/*!
63 @brief Compares two vectors using a user-supplied function; returns {max, iterator}.
64
65 @details
66 Returns {delta, itr} where delta = max|func(first[i], second[i])|,
67 and itr points to the position in first where the maximum occurred.
68*/
69template <typename T, typename U, typename Func>
70auto compare(const std::vector<T> &first, const std::vector<U> &second,
71 Func &func) {
72 assert(first.size() == second.size()); //?
73
74 auto it1 = first.cbegin();
75 auto it2 = second.cbegin();
76 auto largest_eps = 0.0; // double always?
77 auto largest_at = first.cend();
78 for (; it1 != first.cend(); ++it1, ++it2) {
79 const auto eps = func(*it1, *it2);
80 if (std::abs(eps) > std::abs(largest_eps)) {
81 largest_eps = eps;
82 largest_at = it1;
83 }
84 }
85 return std::make_pair(largest_eps, largest_at);
86}
87
88/*!
89 @brief Compares two floating-point vectors element-wise relative to the
90 second; returns {max_eps, iterator}.
91
92 @details
93 Returns {eps, itr} where eps = max|(first-second)/second| (signed),
94 and itr points to the position in first where the maximum occurred.
95*/
96template <typename T>
97auto compare_eps(const std::vector<T> &first, const std::vector<T> &second) {
98 static_assert(
99 std::is_floating_point_v<T>,
100 "In compare_eps(std::vector<T>, std::vector<T>), T must be floating pt");
101 assert(first.size() == second.size()); //?
102
103 auto it1 = first.cbegin();
104 auto it2 = second.cbegin();
105 auto largest_eps = static_cast<T>(0);
106 auto largest_at = first.cend();
107 for (; it1 != first.cend(); ++it1, ++it2) {
108 const auto eps = (*it1 - *it2) / (*it2); // may be +/-inf, fine
109 if (std::abs(eps) > std::abs(largest_eps)) {
110 largest_eps = eps;
111 largest_at = it1;
112 }
113 }
114 return std::make_pair(largest_eps, largest_at);
115}
116
117//==============================================================================
118
119/*!
120 @brief Adds any number of vectors in place (modifies *first).
121
122 @details Resizes *first to the size of the largest vector if needed.
123*/
124template <typename T, typename... Args>
125void add(std::vector<T> *first, const std::vector<T> &second,
126 const Args &...rest) {
127 // re-sizes vector:
128 if (first->size() < second.size())
129 first->resize(second.size());
130
131 for (std::size_t i = 0; i < second.size(); ++i) {
132 (*first)[i] += second[i];
133 }
134 if constexpr (sizeof...(rest) != 0)
135 add(first, rest...);
136}
137
138//! Adds any number of vectors; returns result sized to the largest input.
139template <typename T, typename... Args>
140[[nodiscard]] std::vector<T>
141add(std::vector<T> first, const std::vector<T> &second, const Args &...rest) {
142 add(&first, second, rest...);
143 return first;
144}
145
146//==============================================================================
147
148/*!
149 @brief Multiplies any number of arithmetic vectors in place (modifies *first).
150
151 @details
152 Resizes *first if needed; elements beyond the shorter vector are set to zero.
153*/
154template <typename T, typename... Args>
155void multiply(std::vector<T> *first, const std::vector<T> &second,
156 const Args &...rest) {
157
158 static_assert(std::is_arithmetic_v<T>,
159 "In multiply(std::vector<T>...) : T must be arithmetic");
160
161 // {1,1,1,1}*{1,1} -> {1,1,0,0}; re-sizes vector
162 const auto size = std::min(first->size(), second.size());
163 if (first->size() < second.size())
164 first->resize(second.size());
165
166 for (std::size_t i = 0; i < size; ++i) {
167 (*first)[i] *= second[i];
168 }
169 for (std::size_t i = size; i < first->size(); ++i) {
170 (*first)[i] = static_cast<T>(0);
171 }
172 if constexpr (sizeof...(rest) != 0)
173 multiply(first, rest...);
174}
175
176//! Multiplies any number of vectors; returns result sized to the largest input.
177template <typename T, typename... Args>
178[[nodiscard]] std::vector<T> multiply(std::vector<T> first,
179 const std::vector<T> &second,
180 const Args &...rest) {
181 multiply(&first, second, rest...);
182 return first;
183}
184
185//==============================================================================
186
187/*!
188 @brief Applies func element-wise across any number of vectors in place
189 (modifies *first).
190
191 @details
192 e.g., `qip::compose(std::plus{}, &vo, v2, v3)` is equivalent to
193 `qip::add(&vo, v2, v3)`. Resizes *first if needed.
194*/
195template <typename F, typename T, typename... Args>
196void compose(const F &func, std::vector<T> *first, const std::vector<T> &second,
197 const Args &...rest) {
198 // XXX Comiple=-time constraints on f! Must be T(T,T)!
199
200 if (first->size() < second.size())
201 first->resize(second.size());
202
203 for (std::size_t i = 0; i < second.size(); ++i) {
204 (*first)[i] = func((*first)[i], second[i]);
205 }
206 for (std::size_t i = second.size(); i < first->size(); ++i) {
207 (*first)[i] = func((*first)[i], static_cast<T>(0));
208 }
209 if constexpr (sizeof...(rest) != 0)
210 compose(func, first, rest...);
211}
212
213//! Applies func element-wise across any number of vectors; returns result
214//! sized to the largest input.
215template <typename F, typename T, typename... Args>
216[[nodiscard]] std::vector<T> compose(const F &func, std::vector<T> first,
217 const std::vector<T> &second,
218 const Args &...rest) {
219 compose(func, &first, second, rest...);
220 return first;
221}
222
223//==============================================================================
224
225//! In-place scalar multiplication of a vector; types must match.
226template <typename T>
227void scale(std::vector<T> *vec, T x) {
228 static_assert(std::is_arithmetic_v<T>,
229 "In scale(std::vector<T>, T) : T must be arithmetic");
230 for (auto &v : *vec)
231 v *= x;
232}
233
234//! Scalar multiplication of a vector; types must match.
235template <typename T>
236[[nodiscard]] std::vector<T> scale(std::vector<T> vec, T x) {
237 scale(&vec, x);
238 return vec;
239}
240
241//==============================================================================
242
243/*!
244 @brief Returns a uniformly distributed range [first, last] with number points.
245
246 @details
247 first and last are guaranteed endpoints. number must be at least 2.
248 For integral T the spacing may not be perfectly uniform due to rounding;
249 duplicate values may appear if too many steps are requested.
250*/
251template <typename T = double, typename N = std::size_t>
252std::vector<T> uniform_range(T first, T last, N number) {
253 static_assert(std::is_arithmetic_v<T>,
254 "In uniform_range(T, T, N), T must be arithmetic");
255 static_assert(std::is_integral_v<N>,
256 "In uniform_range(T, T, N), N must be integral");
257 std::vector<T> range;
258 if (number == 0)
259 return range;
260 range.reserve(static_cast<std::size_t>(number));
261 range.push_back(first); // guarentee first is first
262 if (number <= 1)
263 return range;
264 const auto interval = static_cast<double>(last - first);
265 for (N i = 1; i < number - 1; ++i) {
266 const auto eps = static_cast<double>(i) / static_cast<double>(number - 1);
267 const auto value = static_cast<double>(first) + (eps * interval);
268 range.push_back(static_cast<T>(value));
269 }
270 range.push_back(last); // guarentee last is last
271 return range;
272}
273
274/*!
275 @brief Returns a logarithmically distributed range [first, last] with number points.
276
277 @details
278 first and last are guaranteed endpoints. number must be at least 2.
279 For integral T the spacing may not be perfectly logarithmic due to rounding;
280 duplicate values may appear if too many steps are requested.
281*/
282template <typename T = double, typename N = std::size_t>
283std::vector<T> logarithmic_range(T first, T last, N number) {
284 static_assert(std::is_arithmetic_v<T>,
285 "In logarithmic_range(T, T, N), T must be arithmetic");
286 static_assert(std::is_integral_v<N>,
287 "In logarithmic_range(T, T, N), N must be integral");
288
289 std::vector<T> range;
290 if (number == 0)
291 return range;
292 range.reserve(static_cast<std::size_t>(number));
293 range.push_back(first);
294 if (number <= 1)
295 return range;
296 const auto log_ratio =
297 std::log(static_cast<double>(last) / static_cast<double>(first));
298 for (N i = 1; i < number - 1; ++i) {
299 const auto eps = static_cast<double>(i) / static_cast<double>(number - 1);
300 const auto value = static_cast<double>(first) * std::exp(log_ratio * eps);
301 range.push_back(static_cast<T>(value));
302 }
303 range.push_back(last);
304 return range;
305}
306
307/*!
308 @brief Returns a log-linear distributed range [first, last] with number points.
309
310 @details
311 Roughly logarithmic for values below b and linear above b.
312 number must be at least 3. T must be floating point.
313 Not tested for negative values.
314*/
315template <typename T = double, typename N = std::size_t>
316std::vector<T> loglinear_range(T first, T last, T b, N number) {
317 static_assert(std::is_floating_point_v<T>,
318 "In loglinear_range(T, T, T, N), T must be floating point");
319 static_assert(std::is_integral_v<N>,
320 "In loglinear_range(T, T, T, N), N must be integral");
321 assert(number >= 3);
322 assert(first > 0.0 && last > 0.0 && b > 0.0);
323
324 std::vector<T> range;
325 range.reserve(static_cast<std::size_t>(number));
326
327 const auto du =
328 (last - first + b * std::log(last / first)) / (static_cast<T>(number - 1));
329
330 auto next_r = [b](auto u1, auto r_guess) {
331 // Solve eq. u = r + b ln(r) to find r
332 // Use Newtons method
333 // => f(r) = r + b ln(r) - u = 0
334 // dfdr = b(1/r + 1/(b+r))
335 const auto f_u = [b, u1](double tr) { return tr + b * std::log(tr) - u1; };
336 const auto dr = 0.1 * r_guess;
337 const auto delta_targ = r_guess * 1.0e-18;
338 const auto [ri, delta_r] = qip::Newtons(f_u, r_guess, delta_targ, dr, 30);
339 return ri;
340 };
341
342 auto u = first + b * std::log(first);
343 range.push_back(first);
344 for (N i = 1; i < number - 1; ++i) {
345 u += du;
346 range.push_back(next_r(u, range.back()));
347 }
348 range.push_back(last);
349
350 return range;
351}
352
353//==============================================================================
354
355//! Helper: returns first[i] * rest[i] * ... at index i.
356template <typename T, typename... Args>
357constexpr auto multiply_at(std::size_t i, const T &first, const Args &...rest) {
358 // assert(first.size() < i);
359 if constexpr (sizeof...(rest) == 0) {
360 return first[i];
361 } else {
362 return first[i] * multiply_at(i, rest...);
363 }
364}
365
366//! Variadic inner product: sum_i v1[i]*v2[i]*...*vn[i].
367template <typename T, typename... Args>
368constexpr auto inner_product(const T &first, const Args &...rest) {
369 auto res = multiply_at(0, first, rest...);
370 for (std::size_t i = 1; i < first.size(); ++i) {
371 res += multiply_at(i, first, rest...);
372 }
373 return res;
374}
375
376//! Inner product over the subrange [p0, pinf).
377template <typename T, typename... Args>
378auto inner_product_sub(std::size_t p0, std::size_t pinf, const T &first,
379 const Args &...rest) {
380 auto res = decltype(first[0])(0);
381 for (std::size_t i = p0; i < pinf; ++i) {
382 res += multiply_at(i, first, rest...);
383 }
384 return res;
385}
386
387//==============================================================================
388
389//! Applies func to each element of list in place; returns the modified list.
390template <typename F, typename T>
391T apply_to(const F &func, T list) {
392 for (auto &l : list) {
393 l = func(l);
394 }
395 return list;
396}
397
398//==============================================================================
399
400/*!
401 @brief Returns a copy of in containing only elements satisfying condition.
402
403 @details condition must have signature `bool condition(T)`.
404*/
405template <typename T, typename Func>
406std::vector<T> select_if(const std::vector<T> &in, Func condition) {
407 std::vector<T> out;
408 for (const auto &el : in) {
409 if (condition(el)) {
410 out.push_back(el);
411 }
412 }
413 return out;
414}
415
416//! Inserts elements from in into *inout if condition is met.
417template <typename T, typename Func>
418void insert_into_if(const std::vector<T> &in, std::vector<T> *inout,
419 Func condition) {
420 for (const auto &el : in) {
421 if (condition(el)) {
422 inout->push_back(el);
423 }
424 }
425}
426
427//==============================================================================
428
429//! Returns a reversed copy of the vector.
430template <typename T>
431std::vector<T> reverse(std::vector<T> in) {
432
433 std::size_t i = 0ul;
434 std::size_t f = in.size();
435
436 while (f > i + 1) {
437 std::swap(in[i], in[f - 1]);
438 ++i;
439 --f;
440 }
441
442 return in;
443}
444
445//==============================================================================
446
447/*!
448 @brief Mean of a vector.
449
450 @details
451 \f[ \bar x = \frac{1}{N}\sum_i x_i \f]
452*/
453template <typename T>
454T mean(std::vector<T> vec) {
455 T sum{0};
456 for (const auto &x : vec) {
457 sum += x;
458 }
459 return sum / T(vec.size());
460}
461
462/*!
463 @brief Variance using the two-pass method.
464
465 @details
466 \f[ \sigma^2 = \sum_i (x_i - \bar x)^2 / (N - \text{dof}) \f]
467 dof is the degrees of freedom; use dof=1 for sample variance.
468*/
469template <typename T>
470T variance(std::vector<T> vec, std::size_t dof = 0) {
471 assert(dof < vec.size());
472 const auto xbar = mean(vec);
473 T sum{0};
474 for (const auto &x : vec) {
475 const auto del = x - xbar;
476 sum += del * del;
477 }
478 return sum / T(vec.size() - dof);
479}
480
481//! Standard deviation: sqrt(variance).
482template <typename T>
483T sdev(std::vector<T> vec, std::size_t dof = 0) {
484 return std::sqrt(variance(vec, dof));
485}
486
487//! Standard error of the mean: sdev / sqrt(N).
488template <typename T>
489T sem(std::vector<T> vec, std::size_t dof = 0) {
490 return sdev(vec, dof) / std::sqrt(T(vec.size()));
491}
492
493//==============================================================================
494//==============================================================================
495
496/*!
497 @brief Operator overloads for std::vector.
498
499 @details
500 Provides element-wise +, -, *, / between two vectors, and between a vector
501 and a scalar. Use `using namespace qip::overloads;` to enable.
502*/
503namespace overloads {
504
505//! Element-wise addition: a += b, a + b.
506template <typename T>
507std::vector<T> &operator+=(std::vector<T> &a, const std::vector<T> &b) {
508 // The following allows this to work for types that can't be default constructed
509 const auto smaller_size = std::min(a.size(), b.size());
510 if (a.size() < b.size())
511 a.insert(a.end(), b.begin() + long(a.size()), b.end());
512 for (auto i = 0ul; i < smaller_size; ++i) {
513 a[i] += b[i];
514 }
515 return a;
516}
517template <typename T>
518std::vector<T> operator+(std::vector<T> a, const std::vector<T> &b) {
519 return a += b;
520}
521
522//! Element-wise subtraction: a -= b, a - b.
523template <typename T>
524std::vector<T> &operator-=(std::vector<T> &a, const std::vector<T> &b) {
525 const auto size = std::max(a.size(), b.size());
526 a.resize(size);
527 for (auto i = 0ul; i < b.size(); ++i) {
528 a[i] -= b[i];
529 }
530 return a;
531}
532template <typename T>
533std::vector<T> operator-(std::vector<T> a, const std::vector<T> &b) {
534 return a -= b;
535}
536
537// Provide scalar multiplication
538template <typename T, typename U>
539std::vector<T> &operator*=(std::vector<T> &v, U x) {
540 if (x != U{1}) {
541 for (auto &v_i : v) {
542 v_i *= x;
543 }
544 }
545 return v;
546}
547template <typename T, typename U>
548std::vector<T> operator*(std::vector<T> v, U x) {
549 return v *= x;
550}
551template <typename T, typename U>
552std::vector<T> operator*(U x, std::vector<T> v) {
553 return v *= x;
554}
555
556// Provide scalar division
557template <typename T, typename U>
558std::vector<T> &operator/=(std::vector<T> &v, U x) {
559 if (x != U{1}) {
560 for (auto &v_i : v) {
561 v_i /= x;
562 }
563 }
564 return v;
565}
566template <typename T, typename U>
567std::vector<T> operator/(std::vector<T> v, U x) {
568 return v /= x;
569}
570
571// Provide scalar addition
572template <typename T, typename U>
573std::vector<T> &operator+=(std::vector<T> &v, U x) {
574 if (x != U{0}) {
575 for (auto &v_i : v) {
576 v_i += x;
577 }
578 }
579 return v;
580}
581template <typename T, typename U>
582std::vector<T> operator+(std::vector<T> v, U x) {
583 return v += x;
584}
585template <typename T, typename U>
586std::vector<T> operator+(U x, std::vector<T> v) {
587 return v += x;
588}
589
590// Provide scalar subtraction
591template <typename T, typename U>
592std::vector<T> &operator-=(std::vector<T> &v, U x) {
593 if (x != U{0}) {
594 for (auto &v_i : v) {
595 v_i -= x;
596 }
597 }
598 return v;
599}
600template <typename T, typename U>
601std::vector<T> operator-(std::vector<T> v, U x) {
602 return v -= x;
603}
604template <typename T, typename U>
605std::vector<T> operator-(U x, std::vector<T> v) {
606 v *= U{-1};
607 return v += x;
608}
609
610// Provide scalar division, vector in denominator
611template <typename T, typename U>
612std::vector<T> operator/(U x, std::vector<T> v) {
613 for (auto &v_i : v) {
614 v_i = x / v_i;
615 }
616 return v;
617}
618
619//! In-place element-wise multiplication: a*=b => a_i := a_i * b_i.
620template <typename T>
621std::vector<T> &operator*=(std::vector<T> &a, const std::vector<T> &b) {
622 const auto min_size = std::min(a.size(), b.size());
623 for (std::size_t i = 0; i < min_size; ++i) {
624 a[i] *= b[i];
625 }
626 for (std::size_t i = min_size; i < a.size(); ++i) {
627 a[i] = 0.0;
628 }
629 if (a.size() < b.size())
630 a.resize(b.size());
631 return a;
632}
633//! Element-wise multiplication: c=a*b => c_i = a_i * b_i.
634template <typename T>
635std::vector<T> operator*(std::vector<T> a, const std::vector<T> &b) {
636 return a *= b;
637}
638
639//! In-place element-wise division: a/=b => a_i := a_i / b_i.
640template <typename T>
641std::vector<T> &operator/=(std::vector<T> &a, const std::vector<T> &b) {
642 assert(a.size() == b.size());
643 for (std::size_t i = 0; i < a.size(); ++i) {
644 a[i] /= b[i];
645 }
646 return a;
647}
648//! Element-wise division: c=a/b => c_i = a_i / b_i.
649template <typename T>
650std::vector<T> operator/(std::vector<T> a, const std::vector<T> &b) {
651 return a /= b;
652}
653
654} // namespace overloads
655
656} // namespace qip
std::vector< T > & operator+=(std::vector< T > &a, const std::vector< T > &b)
Element-wise addition: a += b, a + b.
Definition Vector.hpp:507
std::vector< T > & operator-=(std::vector< T > &a, const std::vector< T > &b)
Element-wise subtraction: a -= b, a - b.
Definition Vector.hpp:524
General-purpose utility library.
Definition Array.hpp:23
void add(std::vector< T > *first, const std::vector< T > &second, const Args &...rest)
Adds any number of vectors in place (modifies *first).
Definition Vector.hpp:125
std::vector< T > logarithmic_range(T first, T last, N number)
Returns a logarithmically distributed range [first, last] with number points.
Definition Vector.hpp:283
auto compare_eps(const std::vector< T > &first, const std::vector< T > &second)
Compares two floating-point vectors element-wise relative to the second; returns {max_eps,...
Definition Vector.hpp:97
void compose(const F &func, std::vector< T > *first, const std::vector< T > &second, const Args &...rest)
Applies func element-wise across any number of vectors in place (modifies *first).
Definition Vector.hpp:196
void scale(std::vector< T > *vec, T x)
In-place scalar multiplication of a vector; types must match.
Definition Vector.hpp:227
auto inner_product_sub(std::size_t p0, std::size_t pinf, const T &first, const Args &...rest)
Inner product over the subrange [p0, pinf).
Definition Vector.hpp:378
T sem(std::vector< T > vec, std::size_t dof=0)
Standard error of the mean: sdev / sqrt(N).
Definition Vector.hpp:489
std::vector< T > select_if(const std::vector< T > &in, Func condition)
Returns a copy of in containing only elements satisfying condition.
Definition Vector.hpp:406
T mean(std::vector< T > vec)
Mean of a vector.
Definition Vector.hpp:454
T apply_to(const F &func, T list)
Applies func to each element of list in place; returns the modified list.
Definition Vector.hpp:391
std::pair< Real, Real > Newtons(Function f, Real x, Real delta_target=Real{1.0e-6}, Real dx=Real{0.01}, unsigned it_limit=250)
Solves f(x) = 0 using Newton's method; returns {root, error}.
Definition Methods.hpp:55
std::vector< T > merge(std::vector< T > first, const std::vector< T > &second, const Args &...rest)
Merges any number of vectors: {a,b,c},{d,e},{f} -> {a,b,c,d,e,f}.
Definition Vector.hpp:19
std::vector< T > uniform_range(T first, T last, N number)
Returns a uniformly distributed range [first, last] with number points.
Definition Vector.hpp:252
std::vector< T > reverse(std::vector< T > in)
Returns a reversed copy of the vector.
Definition Vector.hpp:431
constexpr auto inner_product(const T &first, const Args &...rest)
Variadic inner product: sum_i v1[i]*v2[i]*...*vn[i].
Definition Vector.hpp:368
T sdev(std::vector< T > vec, std::size_t dof=0)
Standard deviation: sqrt(variance).
Definition Vector.hpp:483
void insert_into_if(const std::vector< T > &in, std::vector< T > *inout, Func condition)
Inserts elements from in into *inout if condition is met.
Definition Vector.hpp:418
void multiply(std::vector< T > *first, const std::vector< T > &second, const Args &...rest)
Multiplies any number of arithmetic vectors in place (modifies *first).
Definition Vector.hpp:155
auto compare(const std::vector< T > &first, const std::vector< T > &second)
Compares two arithmetic vectors element-wise; returns {max_delta, iterator}.
Definition Vector.hpp:41
T variance(std::vector< T > vec, std::size_t dof=0)
Variance using the two-pass method.
Definition Vector.hpp:470
std::vector< T > loglinear_range(T first, T last, T b, N number)
Returns a log-linear distributed range [first, last] with number points.
Definition Vector.hpp:316
constexpr auto multiply_at(std::size_t i, const T &first, const Args &...rest)
Helper: returns first[i] * rest[i] * ... at index i.
Definition Vector.hpp:357