Skip to content

Commit 1f49ed5

Browse files
author
edfink234
committed
Adding suggestions from @ferdymercury
1 parent d43e9c6 commit 1f49ed5

File tree

2 files changed

+103
-54
lines changed

2 files changed

+103
-54
lines changed

math/vecops/inc/ROOT/RVec.hxx

+68-28
Original file line numberDiff line numberDiff line change
@@ -3243,11 +3243,14 @@ RVec<typename RVec<T>::size_type> Enumerate(const RVec<T> &v)
32433243
}
32443244

32453245
/**
3246-
* \brief Produce RVec with N evenly-spaced entries from start to end inclusive.
3246+
* \brief Produce RVec with N evenly-spaced entries from start to end.
32473247
*
3248-
* This function generates a vector of evenly spaced values, starting at \p start and ending at \p end,
3249-
* with exactly \p n elements. The spacing is computed as
3250-
* \f$\text{step} = \frac{\text{end} - \text{start}}{n-1}\f$, so that the vector includes both endpoints.
3248+
* This function generates a vector of evenly spaced values, starting at \p start and (depending on the
3249+
* \p endpoint parameter) either including or excluding \p end. If \p endpoint is true (default),
3250+
* the vector contains \p n values with \p end as the final element, and the spacing is computed as
3251+
* \f$\text{step} = \frac{\text{end} - \text{start}}{n-1}\f$. If \p endpoint is false,
3252+
* the sequence consists of n values computed as if there were n+1 evenly spaced samples, with the final
3253+
* value (\p end) omitted; in this case, \f$\text{step} = \frac{\text{end} - \text{start}}{n}\f$.
32513254
*
32523255
* The function is templated to allow for different arithmetic types. The deduced type \c Common_t is
32533256
* determined as follows: if the common type of \p T1 and \p T2 is a floating point type, that type is used;
@@ -3259,17 +3262,28 @@ RVec<typename RVec<T>::size_type> Enumerate(const RVec<T> &v)
32593262
* floating point type, or double otherwise.
32603263
*
32613264
* \param start The first value in the sequence.
3262-
* \param end The last value in the sequence.
3263-
* \param n The number of evenly spaced entries to produce (must be greater than 0).
3265+
* \param end The last value in the sequence if \p endpoint is true; otherwise, \p end is excluded.
3266+
* \param n The number of evenly spaced entries to produce.
3267+
* \param endpoint If true (default), \p end is included as the final element; if false, \p end is excluded.
32643268
*
3265-
* \return A vector (RVec<Common_t>) containing \p n evenly spaced values from \p start to \p end inclusive.
3269+
* \return A vector (RVec<Common_t>) containing \p n evenly spaced values.
32663270
*
32673271
* \note If \p n is 1, the resulting vector will contain only the value \p start.
32683272
* \note The check `if (!n || (n > std::numeric_limits<long long>::max()))` is used to ensure that:
32693273
* - n is nonzero, and
32703274
* - n does not exceed std::numeric_limits<long long>::max(), which would indicate that a negative range (or other arithmetic issue)
32713275
* has resulted in an extremely large unsigned value, thereby preventing an attempt to reserve an absurd
32723276
* amount of memory.
3277+
* \note If the template parameter \c Common_t is explicitly overridden with an integral type, the arithmetic may cause rounding issues. Consequently, the resulting sequence may not end exactly at \p end. To ensure that the sequence ends exactly at \p end, consider casting the result as follows: `RVec<integral_type>(Linspace(...))`. This behavior is different than NumPy in Python.
3278+
*
3279+
* \par C++23 Enumerate Support:
3280+
* With C++23, you can use the range-based enumerate view to iterate over the resulting vector with both the index
3281+
* and the value, similar to Python's `enumerate`. For example:
3282+
* ~~~{.cpp}
3283+
* for (auto const [index, val] : std::views::enumerate(ROOT::VecOps::Linspace(6, 10, 16))) {
3284+
* // Process index and val.
3285+
* }
3286+
* ~~~
32733287
*
32743288
* \par Example code, at the ROOT prompt:
32753289
* ~~~{.cpp}
@@ -3278,12 +3292,14 @@ RVec<typename RVec<T>::size_type> Enumerate(const RVec<T> &v)
32783292
* // { -1, 0.5, 2, 3.5, 5 }
32793293
* cout << Linspace(3, 12, 5) << "\n";
32803294
* // { 3, 5.25, 7.5, 9.75, 12 }
3281-
* cout << Linspace(1.4, 13.66, 5) << "\n";
3282-
* // { 1.4, 4.465, 7.53, 10.595, 13.66 }
3295+
* cout << Linspace(3, 12, 5, false) << "\n";
3296+
* // { 3, 4.8, 6.6, 8.4, 10.2 }
3297+
* Linspace<int, int, int>(1, 10, 3) << "\n";
3298+
* // { 1, 5, 9 }
32833299
* ~~~
32843300
*/
32853301
template <typename T1 = double, typename T2 = double, typename Common_t = std::conditional_t<std::is_floating_point_v<std::common_type_t<T1, T2>>, std::common_type_t<T1, T2>, double>>
3286-
inline RVec<Common_t> Linspace(T1 start, T2 end, unsigned long long n = 128)
3302+
inline RVec<Common_t> Linspace(T1 start, T2 end, unsigned long long n = 128, const bool endpoint = true)
32873303
{
32883304
RVec<Common_t> temp;
32893305

@@ -3292,7 +3308,7 @@ inline RVec<Common_t> Linspace(T1 start, T2 end, unsigned long long n = 128)
32923308
return temp;
32933309
}
32943310

3295-
Common_t step = (static_cast<Common_t>(end) - static_cast<Common_t>(start)) / static_cast<Common_t>(n - 1);
3311+
Common_t step = (static_cast<Common_t>(end) - static_cast<Common_t>(start)) / static_cast<Common_t>(n - endpoint);
32963312
temp.reserve(n);
32973313
temp.push_back(static_cast<Common_t>(start));
32983314
for (unsigned long long i = 1; i < n; i++)
@@ -3303,38 +3319,46 @@ inline RVec<Common_t> Linspace(T1 start, T2 end, unsigned long long n = 128)
33033319
}
33043320

33053321
/**
3306-
* \brief Produce RVec with n log-spaced entries from base^{start} to base^{end} inclusive.
3322+
* \brief Produce RVec with n log-spaced entries from base^{start} to base^{end}.
33073323
*
3308-
* This function generates a vector of values where the exponents are evenly spaced,
3309-
* and then returns the corresponding values of base raised to these exponents.
3310-
* More specifically, it creates a vector with n entries where the first element is
3311-
* \f$base^{start}\f$ and the last element is \f$base^{end}\f$, with the intermediate
3312-
* values computed such that the exponents are evenly spaced between start and end.
3324+
* This function generates a vector of values where the exponents are evenly spaced, and then returns the
3325+
* corresponding values of base raised to these exponents. If \p endpoint is true (default), the vector
3326+
* contains \p n values with the last element equal to \f$base^{end}\f$. If \p endpoint is false, the
3327+
* sequence is computed as if there were n+1 evenly spaced samples over the interval in the exponent space,
3328+
* and the final value (\f$base^{end}\f$) is excluded, resulting in a sequence of n values.
33133329
*
3314-
* The function is templated to allow for different arithmetic types. The deduced type
3315-
* \c Common_t is determined as follows: if the common type of \p T1, \p T2, and \p T3 is a
3316-
* floating point type, that type is used; otherwise, the arithmetic is performed using \c double.
3330+
* The function is templated to allow for different arithmetic types. The deduced type \c Common_t is determined as follows:
3331+
* if the common type of \p T1, \p T2, and \p T3 is a floating point type, that type is used; otherwise, the arithmetic is performed using \c double.
33173332
*
33183333
* \tparam T1 Type of the start exponent. Default is double.
33193334
* \tparam T2 Type of the end exponent. Default is double.
33203335
* \tparam T3 Type of the base (and auxiliary type for deducing common type). Default is double.
3321-
* \tparam Common_t Deduced type used for arithmetic, which is
3322-
* std::common_type_t<T1, T2, T3> if that is a floating point type, or double otherwise.
3336+
* \tparam Common_t Deduced type used for arithmetic, which is std::common_type_t<T1, T2, T3> if that is a floating point type, or double otherwise.
33233337
*
3324-
* \param start The exponent corresponding to the first element (\f$base^{start}\f$).
3325-
* \param end The exponent corresponding to the last element (\f$base^{end}\f$).
3338+
* \param start The exponent corresponding to the first element (i.e., the first element is \f$base^{start}\f$).
3339+
* \param end The exponent corresponding to the final element if \p endpoint is true; otherwise, \p end is excluded.
33263340
* \param n The number of log-spaced entries to produce.
33273341
* \param base The base to be used in the exponentiation (default is 10.0).
3342+
* \param endpoint If true (default), \f$base^{end}\f$ is included as the final element; if false, \f$base^{end}\f$ is excluded.
33283343
*
3329-
* \return A vector (RVec<Common_t>) containing n values, where the i-th element is
3330-
* computed as \f$base^{(start + i \cdot \text{step})}\f$, with \f$\text{step} = \frac{end - start}{n - 1}\f$.
3344+
* \return A vector (RVec<Common_t>) containing n log-spaced values.
33313345
*
33323346
* \note If \p n is 1, the resulting vector will contain only the value \f$base^{start}\f$.
33333347
* \note The check `if (!n || (n > std::numeric_limits<long long>::max()))` is used to ensure that:
33343348
* - n is nonzero, and
33353349
* - n does not exceed std::numeric_limits<long long>::max(), which would indicate that a negative range (or other arithmetic issue)
33363350
* has resulted in an extremely large unsigned value, thereby preventing an attempt to reserve an absurd
33373351
* amount of memory.
3352+
* \note If the template parameter \c Common_t is explicitly overridden with an integral type, the arithmetic may introduce rounding errors, and as a consequence, the sequence may not end exactly at \f$base^{end}\f>. To ensure that the final element is exactly \f$base^{end}\f>, consider casting the result as follows: `RVec<integral_type>(Logspace(...))`. This behavior is different than NumPy in Python.
3353+
*
3354+
* \par C++23 Enumerate Support:
3355+
* With C++23, you can use the range-based enumerate view to iterate over the resulting vector with both the index
3356+
* and the value, similar to Python's `enumerate`. For example:
3357+
* ~~~{.cpp}
3358+
* for (auto const [index, val] : std::views::enumerate(ROOT::VecOps::Logspace(4, 10, 12))) {
3359+
* // Process index and val.
3360+
* }
3361+
* ~~~
33383362
*
33393363
* \par Example code, at the ROOT prompt:
33403364
* ~~~{.cpp}
@@ -3345,10 +3369,14 @@ inline RVec<Common_t> Linspace(T1 start, T2 end, unsigned long long n = 128)
33453369
* // { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
33463370
* cout << Logspace(0, 0, 0) << '\n';
33473371
* // { }
3372+
* cout << Logspace(4, 10, 12, 10.0, false) << '\n';
3373+
* // { 10000, 31622.8, 100000, 316228, 1e+06, 3.16228e+06, 1e+07, 3.16228e+07, 1e+08, 3.16228e+08, 1e+09, 3.16228e+09 }
3374+
* cout << Logspace<int, int, int, int>(1, 5, 3) << '\n';
3375+
* // { 10, 1000, 100000 }
33483376
* ~~~
33493377
*/
33503378
template <typename T1 = double, typename T2 = double, typename T3 = double, typename Common_t = std::conditional_t<std::is_floating_point_v<std::common_type_t<T1, T2, T3>>, std::common_type_t<T1, T2, T3>, double>>
3351-
inline RVec<Common_t> Logspace(T1 start, T2 end, unsigned long long n = 128, T3 base = 10.0)
3379+
inline RVec<Common_t> Logspace(T1 start, T2 end, unsigned long long n = 128, T3 base = 10.0, const bool endpoint = true)
33523380
{
33533381
RVec<Common_t> temp;
33543382

@@ -3360,7 +3388,7 @@ inline RVec<Common_t> Logspace(T1 start, T2 end, unsigned long long n = 128, T3
33603388
Common_t start_c = static_cast<Common_t>(start);
33613389
Common_t end_c = static_cast<Common_t>(end);
33623390
Common_t base_c = static_cast<Common_t>(base);
3363-
Common_t step = static_cast<Common_t>(end_c-start_c)/static_cast<Common_t>(n-1);
3391+
Common_t step = static_cast<Common_t>(end_c - start_c)/static_cast<Common_t>(n - endpoint);
33643392
temp.reserve(n);
33653393
temp.push_back(static_cast<Common_t>(std::pow(base_c, start_c)));
33663394
for (unsigned long long i = 1; i < n; i++)
@@ -3404,6 +3432,16 @@ inline RVec<Common_t> Logspace(T1 start, T2 end, unsigned long long n = 128, T3
34043432
* - n does not exceed std::numeric_limits<long long>::max(), which would indicate that a negative range (or other arithmetic issue)
34053433
* has resulted in an extremely large unsigned value, thereby preventing an attempt to reserve an absurd
34063434
* amount of memory.
3435+
* \note If the template parameter \c Common_t is explicitly overridden with an integral type, the arithmetic may introduce rounding errors, and as a consequence, the produced sequence may not strictly adhere to the intended progression. If an exact sequence is desired, consider casting the result as follows: `RVec<integral_type>(Arange(...))`. This behavior is different than NumPy in Python.
3436+
*
3437+
* \par C++23 Enumerate Support:
3438+
* With C++23, you can use the range-based enumerate view to iterate over the resulting vector with both the index
3439+
* and the value, similar to Python's `enumerate`. For example:
3440+
* ~~~{.cpp}
3441+
* for (auto const [index, val] : std::views::enumerate(ROOT::VecOps::Arange(1, 13, 5))) {
3442+
* // Process index and val.
3443+
* }
3444+
* ~~~
34073445
*
34083446
* \par Example code, at the ROOT prompt:
34093447
* ~~~{.cpp}
@@ -3414,6 +3452,8 @@ inline RVec<Common_t> Logspace(T1 start, T2 end, unsigned long long n = 128, T3
34143452
* // { -7, -3, 1, 5, 9, 13, 17 }
34153453
* cout << Arange(1, 13, 5) << '\n';
34163454
* // { 1, 6, 11 }
3455+
* cout << Arange<unsigned int, unsigned int, unsigned int, unsigned int>(5, 9, 1) << '\n';
3456+
* // { 5, 6, 7, 8 }
34173457
* ~~~
34183458
*/
34193459
template <typename T1 = double, typename T2 = double, typename T3 = double, typename Common_t = std::conditional_t<std::is_floating_point_v<std::common_type_t<T1, T2, T3>>, std::common_type_t<T1, T2, T3>, double>>

0 commit comments

Comments
 (0)