Skip to content

Commit

Permalink
* Change cartesian_product::ra_inc to use checked numeric operations.
Browse files Browse the repository at this point in the history
* Change `cartesian_product::ra_inc` to not treat cursors as numeric types.
* Add new `num::checked_(div|mod)` and associated divide-by-zero policies.
  • Loading branch information
brycelelbach committed Jul 31, 2023
1 parent 53d051e commit 8cb17a1
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 12 deletions.
27 changes: 27 additions & 0 deletions include/flux/core/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#define FLUX_OVERFLOW_POLICY_WRAP 11
#define FLUX_OVERFLOW_POLICY_IGNORE 12

#define FLUX_DIVIDE_BY_ZERO_POLICY_ERROR 100
#define FLUX_DIVIDE_BY_ZERO_POLICY_IGNORE 101

// Default error policy is terminate
#define FLUX_DEFAULT_ERROR_POLICY FLUX_ERROR_POLICY_TERMINATE

Expand All @@ -29,6 +32,13 @@
# define FLUX_DEFAULT_OVERFLOW_POLICY FLUX_OVERFLOW_POLICY_ERROR
#endif // NDEBUG

// Default divide by zero policy is error in debug builds, ignore in release builds
#ifdef NDEBUG
# define FLUX_DEFAULT_DIVIDE_BY_ZERO_POLICY FLUX_DIVIDE_BY_ZERO_POLICY_IGNORE
#else
# define FLUX_DEFAULT_DIVIDE_BY_ZERO_POLICY FLUX_DIVIDE_BY_ZERO_POLICY_ERROR
#endif // NDEBUG

// Select which error policy to use
#if defined(FLUX_TERMINATE_ON_ERROR)
# define FLUX_ERROR_POLICY FLUX_ERROR_POLICY_TERMINATE
Expand Down Expand Up @@ -63,6 +73,15 @@
# define FLUX_OVERFLOW_POLICY FLUX_DEFAULT_OVERFLOW_POLICY
#endif // FLUX_ERROR_ON_OVERFLOW

// Select which overflow policy to use
#if defined(FLUX_ERROR_ON_DIVIDE_BY_ZERO)
# define FLUX_DIVIDE_BY_ZERO_POLICY FLUX_DIVIDE_BY_ZERO_POLICY_ERROR
#elif defined(FLUX_IGNORE_DIVIDE_BY_ZERO)
# define FLUX_DIVIDE_BY_ZERO_POLICY FLUX_DIVIDE_BY_ZERO_POLICY_IGNORE
#else
# define FLUX_DIVIDE_BY_ZERO_POLICY FLUX_DEFAULT_DIVIDE_BY_ZERO_POLICY
#endif // FLUX_ERROR_ON_DIVIDE_BY_ZERO

// Default int_t is ptrdiff_t
#define FLUX_DEFAULT_INT_TYPE std::ptrdiff_t

Expand All @@ -86,6 +105,11 @@ enum class overflow_policy {
error = FLUX_OVERFLOW_POLICY_ERROR
};

enum class divide_by_zero_policy {
ignore = FLUX_DIVIDE_BY_ZERO_POLICY_IGNORE,
error = FLUX_DIVIDE_BY_ZERO_POLICY_ERROR
};

namespace config {

FLUX_EXPORT
Expand All @@ -99,6 +123,9 @@ inline constexpr error_policy on_error = static_cast<error_policy>(FLUX_ERROR_PO
FLUX_EXPORT
inline constexpr overflow_policy on_overflow = static_cast<overflow_policy>(FLUX_OVERFLOW_POLICY);

FLUX_EXPORT
inline constexpr divide_by_zero_policy on_divide_by_zero = static_cast<divide_by_zero_policy>(FLUX_DIVIDE_BY_ZERO_POLICY);

FLUX_EXPORT
inline constexpr bool print_error_on_terminate = FLUX_PRINT_ERROR_ON_TERMINATE;

Expand Down
38 changes: 38 additions & 0 deletions include/flux/core/numeric.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,44 @@ inline constexpr auto checked_mul =
}
};

inline constexpr auto checked_div =
[]<std::signed_integral T>(T lhs, T rhs,
std::source_location loc = std::source_location::current())
-> T
{
if (std::is_constant_evaluated()) {
return lhs / rhs;
} else {
if constexpr (config::on_divide_by_zero == divide_by_zero_policy::ignore) {
return lhs / rhs;
} else {
if (rhs == 0) {
runtime_error("divide by zero", loc);
}
return lhs / rhs;
}
}
};

inline constexpr auto checked_mod =
[]<std::signed_integral T>(T lhs, T rhs,
std::source_location loc = std::source_location::current())
-> T
{
if (std::is_constant_evaluated()) {
return lhs % rhs;
} else {
if constexpr (config::on_divide_by_zero == divide_by_zero_policy::ignore) {
return lhs % rhs;
} else {
if (rhs == 0) {
runtime_error("divide by zero", loc);
}
return lhs % rhs;
}
}
};

} // namespace flux::num

#endif
22 changes: 10 additions & 12 deletions include/flux/op/cartesian_product.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,23 +95,19 @@ struct cartesian_product_traits_base {
return cur;

auto& base = std::get<I>(self.bases_);
auto& this_index = std::get<I>(cur);
const auto this_index = flux::distance(base, flux::first(base), std::get<I>(cur));
auto new_index = num::checked_add(this_index, offset);
auto this_size = flux::size(base);

this_index += offset;

if (this_index >= 0 && this_index < this_size)
return cur;

// If the new index overflows the maximum or underflows zero, calculate the carryover and fix it.
else {
offset = this_index / this_size;
this_index %= this_size;
if (new_index < 0 || new_index >= this_size) {
offset = num::checked_div(new_index, this_size);
new_index = num::checked_mod(new_index, this_size);

// Correct for negative index which may happen when underflowing.
if (this_index < 0) {
this_index += this_size;
--offset;
if (new_index < 0) {
new_index = num::checked_add(new_index, this_size);
offset = num::checked_sub(offset, flux::distance_t(1));
}

// Call the next level down if necessary.
Expand All @@ -122,6 +118,8 @@ struct cartesian_product_traits_base {
}
}

flux::inc(base, std::get<I>(cur), num::checked_sub(new_index, this_index));

return cur;
}

Expand Down

0 comments on commit 8cb17a1

Please sign in to comment.