Line data Source code
1 : // SPDX-License-Identifier: BSD-3-Clause
2 : // Copyright (c) 2023 Scipp contributors (https://github.com/scipp)
3 : /// @file
4 : /// @author Piotr Rozyczko
5 : #pragma once
6 :
7 : #include <cmath>
8 :
9 : #include "scipp/common/numeric.h"
10 : #include "scipp/common/overloaded.h"
11 : #include "scipp/core/eigen.h"
12 : #include "scipp/core/element/arg_list.h"
13 : #include "scipp/core/transform_common.h"
14 : #include "scipp/core/values_and_variances.h"
15 :
16 : /// Operators to be used with transform and transform_in_place to implement
17 : /// operations for Variable.
18 : namespace scipp::core::element {
19 :
20 : using isclose_types_t = arg_list_t<
21 : double, float, int64_t, int32_t, std::tuple<float, float, double>,
22 : std::tuple<int64_t, int64_t, double>, std::tuple<int32_t, int32_t, double>,
23 : std::tuple<int32_t, int64_t, double>, std::tuple<int64_t, int32_t, double>,
24 : std::tuple<int64_t, int32_t, int64_t>,
25 : std::tuple<int32_t, int32_t, int64_t>,
26 : std::tuple<int32_t, int64_t, int64_t>>;
27 :
28 449 : constexpr auto isclose_units = [](const units::Unit &x, const units::Unit &y,
29 : const units::Unit &t) {
30 449 : expect::equals(x, y);
31 449 : expect::equals(x, t);
32 449 : return units::none;
33 : };
34 :
35 : constexpr auto isclose = overloaded{
36 : transform_flags::expect_no_variance_arg_t<2>{}, isclose_types_t{},
37 21804 : isclose_units, [](const auto &x, const auto &y, const auto &t) {
38 : using std::abs;
39 21804 : return abs(x - y) <= t;
40 : }};
41 :
42 : constexpr auto isclose_equal_nan = overloaded{
43 : transform_flags::expect_no_variance_arg_t<2>{}, isclose_types_t{},
44 268 : isclose_units, [](const auto &x, const auto &y, const auto &t) {
45 : using std::abs;
46 : using numeric::isnan;
47 : using numeric::isinf;
48 : using numeric::signbit;
49 268 : if (isnan(x) && isnan(y))
50 1 : return true;
51 267 : if (isinf(x) && isinf(y) && signbit(x) == signbit(y))
52 0 : return true;
53 267 : return abs(x - y) <= t;
54 : }};
55 :
56 : struct comparison_types_t {
57 : constexpr void operator()() const noexcept;
58 : using types = decltype(std::tuple_cat(std::declval<arithmetic_type_pairs>(),
59 : std::tuple<bool>{},
60 : std::tuple<core::time_point>{}));
61 : };
62 :
63 : struct equality_types_t {
64 : constexpr void operator()() const noexcept;
65 : using types = decltype(std::tuple_cat(
66 : comparison_types_t::types{}, std::tuple<std::string>{},
67 : std::tuple<Eigen::Vector3d>{}, std::tuple<Eigen::Matrix3d>{},
68 : std::tuple<Eigen::Affine3d>{}, std::tuple<Quaternion>{},
69 : std::tuple<Translation>{}));
70 : };
71 :
72 : // Allow variance broadcasts because we just want to check for numeric equality.
73 : // For inequalities, the variances are ignored anyway.
74 : // See issue #3266
75 : constexpr auto comparison = overloaded{
76 : transform_flags::no_out_variance, transform_flags::force_variance_broadcast,
77 5665 : [](const units::Unit &x, const units::Unit &y) {
78 5666 : expect::equals(x, y);
79 5664 : return units::none;
80 : }};
81 :
82 : constexpr auto inequality = overloaded{comparison_types_t{}, comparison};
83 :
84 : constexpr auto equality = overloaded{equality_types_t{}, comparison};
85 :
86 : constexpr auto less = overloaded{
87 : inequality,
88 596 : [](const auto &x, const auto &y) { return x < y; },
89 : };
90 :
91 : constexpr auto greater = overloaded{
92 : inequality,
93 5092 : [](const auto &x, const auto &y) { return x > y; },
94 : };
95 :
96 : constexpr auto less_equal = overloaded{
97 : inequality,
98 1109 : [](const auto &x, const auto &y) { return x <= y; },
99 : };
100 :
101 : constexpr auto greater_equal =
102 1835 : overloaded{inequality, [](const auto &x, const auto &y) { return x >= y; }};
103 :
104 : constexpr auto equal = overloaded{
105 : equality,
106 1487 : [](const auto &x, const auto &y) {
107 : using numeric::operator==;
108 1487 : return x == y;
109 : },
110 : };
111 : constexpr auto not_equal =
112 356 : overloaded{equality, [](const auto &x, const auto &y) {
113 : using numeric::operator!=;
114 356 : return x != y;
115 : }};
116 :
117 : constexpr auto max_equals =
118 : overloaded{arg_list<double, float, int64_t, int32_t, bool, time_point>,
119 : transform_flags::expect_in_variance_if_out_variance,
120 49598064 : [](auto &&a, const auto &b) {
121 : using numeric::isnan;
122 : using std::max;
123 49598064 : if (isnan(b))
124 0 : a = b;
125 49598064 : else if (!isnan(a))
126 49598064 : a = max(a, b);
127 49598064 : }};
128 :
129 : constexpr auto nanmax_equals =
130 : overloaded{arg_list<double, float, int64_t, int32_t, bool, time_point>,
131 : transform_flags::expect_in_variance_if_out_variance,
132 480 : [](auto &&a, const auto &b) {
133 : using numeric::isnan;
134 : using std::max;
135 480 : if (isnan(a))
136 0 : a = b;
137 480 : if (!isnan(b))
138 418 : a = max(a, b);
139 480 : }};
140 :
141 : constexpr auto min_equals =
142 : overloaded{arg_list<double, float, int64_t, int32_t, bool, time_point>,
143 : transform_flags::expect_in_variance_if_out_variance,
144 49597242 : [](auto &&a, const auto &b) {
145 : using numeric::isnan;
146 : using std::min;
147 49597242 : if (isnan(b))
148 0 : a = b;
149 49597242 : else if (!isnan(a))
150 49597242 : a = min(a, b);
151 49597242 : }};
152 :
153 : constexpr auto nanmin_equals =
154 : overloaded{arg_list<double, float, int64_t, int32_t, bool, time_point>,
155 : transform_flags::expect_in_variance_if_out_variance,
156 480 : [](auto &&a, const auto &b) {
157 : using numeric::isnan;
158 : using std::min;
159 480 : if (isnan(a))
160 0 : a = b;
161 480 : if (!isnan(b))
162 418 : a = min(a, b);
163 480 : }};
164 :
165 : } // namespace scipp::core::element
|