OpenKalman
comparison-operators.hpp
Go to the documentation of this file.
1 /* This file is part of OpenKalman, a header-only C++ library for
2  * Kalman filters and b recursive filters.
3  *
4  * Copyright (c) 2020-2025 Christopher Lee Ogden <ogden@gatech.edu>
5  *
6  * This Source Code Form is subject to the terms of the Mozilla Public
7  * License, v. 2.0. If a copy of the MPL was not distributed with this
8  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9  */
10 
16 #ifndef OPENKALMAN_COORDINATES_COMPARISON_OPERATORS_HPP
17 #define OPENKALMAN_COORDINATES_COMPARISON_OPERATORS_HPP
18 
19 #ifdef __cpp_impl_three_way_comparison
20 #include <compare>
21 #endif
22 #include <iostream>
23 #include <type_traits>
26 #include "collections/views/single.hpp"
32 
34 {
35  namespace detail
36  {
37 #ifdef __cpp_impl_three_way_comparison
38  using ordering = std::partial_ordering;
39 
40  template<typename A, typename B>
41  static constexpr std::partial_ordering
42  ordering_compare(const A& a, const B& b)
43  {
44  return a <=> b;
45  }
46 #else
47  namespace cmp_cat
48  {
49  using type = signed char;
50  enum struct Ord : type { equivalent = 0, less = -1, greater = 1 };
51  enum struct Ncmp : type { unordered = 2 };
52  }
53 
54  struct ordering
55  {
56  cmp_cat::type m_value;
57 
58  constexpr explicit
59  ordering(cmp_cat::Ord v) noexcept : m_value(cmp_cat::type(v)) {}
60 
61  constexpr explicit
62  ordering(cmp_cat::Ncmp v) noexcept : m_value(cmp_cat::type(v)) {}
63 
64  public:
65 
66  static const ordering less;
67  static const ordering equivalent;
68  static const ordering greater;
69  static const ordering unordered;
70 
71  [[nodiscard]] friend constexpr bool
72  operator==(ordering v, ordering w) noexcept { return v.m_value == w.m_value; }
73  };
74 
75  constexpr ordering ordering::less(cmp_cat::Ord::less);
76  constexpr ordering ordering::equivalent(cmp_cat::Ord::equivalent);
77  constexpr ordering ordering::greater(cmp_cat::Ord::greater);
78  constexpr ordering ordering::unordered(cmp_cat::Ncmp::unordered);
79 
80 
81  template<typename A, typename B>
82  static constexpr ordering
83  ordering_compare(const A& a, const B& b)
84  {
85  return
86  a < b ? ordering::less :
87  a > b ? ordering::greater :
88  a == b ? ordering::equivalent :
89  ordering::unordered;
90  }
91 #endif
92 
93 
94  template<std::size_t ai = 0, std::size_t bi = 0, std::size_t abank = 0, std::size_t bbank = 0, typename A, typename B>
95  constexpr auto
96  compare_fixed(const A& a, const B& b)
97  {
98  if constexpr (ai < collections::size_of_v<A> and bi < collections::size_of_v<B>)
99  {
100  using Ai = std::tuple_element_t<ai, A>;
101  using Bi = std::tuple_element_t<bi, B>;
102  constexpr bool ae = euclidean_pattern<Ai>, be = euclidean_pattern<Bi>;
103  if constexpr (ae and be) return compare_fixed<ai + 1, bi + 1, abank + dimension_of_v<Ai>, bbank + dimension_of_v<Bi>>(a, b);
104  else if constexpr (ae) return compare_fixed<ai + 1, bi, abank + dimension_of_v<Ai>, bbank>(a, b);
105  else if constexpr (be) return compare_fixed<ai, bi + 1, abank, bbank + dimension_of_v<Bi>>(a, b);
106  else
107  {
108  if constexpr (abank == bbank) if (internal::get_hash_code(collections::get<ai>(a)) == internal::get_hash_code(collections::get<bi>(b)))
109  return compare_fixed<ai + 1, bi + 1>(a, b);
110  return ordering::unordered;
111  }
112  }
113  else if constexpr (ai < collections::size_of_v<A>) // bi >= collections::size_of_v<B>
114  {
115  using Ai = std::tuple_element_t<ai, A>;
116  if (euclidean_pattern<Ai>) return compare_fixed<ai + 1, bi, abank + dimension_of_v<Ai>, bbank>(a, b);
117  if (values::to_number(abank) >= values::to_number(bbank)) return ordering::greater;
118  return ordering::unordered;
119  }
120  else if constexpr (bi < collections::size_of_v<B>) // ai >= collections::size_of_v<A>
121  {
122  using Bi = std::tuple_element_t<bi, B>;
123  if (euclidean_pattern<Bi>) return compare_fixed<ai, bi + 1, abank, bbank + dimension_of_v<Bi>>(a, b);
124  if (values::to_number(abank) <= values::to_number(bbank)) return ordering::less;
125  return ordering::unordered;
126  }
127  else
128  {
129  // not ai_in_range and not bi_in_range:
130  return ordering_compare(values::to_number(abank), values::to_number(bbank));
131  }
132  }
133 
134 
135  template<typename A, typename B>
136  constexpr ordering
137  compare_impl(const A& a, const B& b, std::size_t ai = 0, std::size_t bi = 0, std::size_t abank = 0, std::size_t bbank = 0)
138  {
139  bool ai_in_range = ai < values::to_number(get_size(a));
140  bool bi_in_range = bi < values::to_number(get_size(b));
141 
142  if (ai_in_range and bi_in_range)
143  {
144  auto a_i = internal::get_descriptor_collection_element(a, ai);
145  auto b_i = internal::get_descriptor_collection_element(b, bi);
146  bool ae = get_is_euclidean(a_i), be = get_is_euclidean(b_i);
147  if (ae or be) return compare_impl(a, b, (ae ? ai + 1 : ai), (be ? bi + 1 : bi),
148  (ae ? abank + get_dimension(a_i) : abank), (be ? bbank + get_dimension(b_i) : bbank));
149  if (internal::get_hash_code(a_i) == internal::get_hash_code(b_i) and abank == bbank) return compare_impl(a, b, ai + 1, bi + 1);
150  return ordering::unordered;
151  }
152  if (ai_in_range) // not bi_in_range
153  {
154  auto a_i = internal::get_descriptor_collection_element(a, ai);
155  if (get_is_euclidean(a_i)) return compare_impl(a, b, ai + 1, bi, abank + get_dimension(a_i), bbank);
156  if (abank >= bbank) return ordering::greater;
157  return ordering::unordered;
158  }
159  if (bi_in_range) // not ai_in_range
160  {
161  auto b_i = internal::get_descriptor_collection_element(b, bi);
162  if (get_is_euclidean(b_i)) return compare_impl(a, b, ai, bi + 1, abank, bbank + get_dimension(b_i));
163  if (abank <= bbank) return ordering::less;
164  return ordering::unordered;
165  }
166  // not ai_in_range and not bi_in_range:
167  return ordering_compare(abank, bbank);
168  }
169 
170  } // namespace detail
171 
172 
177 #if defined(__cpp_concepts) and defined(__cpp_impl_three_way_comparison)
178  template<pattern A, pattern B>
179  constexpr std::partial_ordering
180  operator<=>(const A& a, const B& b)
181  {
183  if (get_dimension(a) == 0) return std::partial_ordering::less;
184  if (get_dimension(b) == 0) return std::partial_ordering::greater;
185 
186  if constexpr (descriptor<A> and descriptor<B>)
187  {
188  if (internal::get_hash_code(a) == internal::get_hash_code(b)) return std::partial_ordering::equivalent;
189  return std::partial_ordering::unordered;
190  }
191  else if constexpr (fixed_pattern<A> and fixed_pattern<B>)
192  {
193  if constexpr (descriptor<A>)
194  return detail::compare_fixed(OpenKalman::views::single(a), b);
195  else if constexpr (descriptor<B>)
196  return detail::compare_fixed(a, OpenKalman::views::single(b));
197  else
198  return detail::compare_fixed(a, b);
199  }
200  else
201  {
202  if constexpr (descriptor<A>)
203  return detail::compare_impl(OpenKalman::views::single(a), b);
204  else if constexpr (descriptor<B>)
205  return detail::compare_impl(a, OpenKalman::views::single(b));
206  else
207  return detail::compare_impl(a, b);
208  }
209  }
210 
211 
215  template<pattern A, pattern B>
216  constexpr bool
217  operator==(const A& a, const B& b)
218  {
219  return std::is_eq(a <=> b);
220  }
221 
222 
223 #else
224  template<typename A, typename B, std::enable_if_t<pattern<A> and pattern<B>, int> = 0>
225  constexpr bool operator==(const A& a, const B& b)
226  {
228 
229  if constexpr (descriptor<A> and descriptor<B>) return internal::get_hash_code(a) == internal::get_hash_code(b);
230  else if constexpr (descriptor<A>) return std::array {a} == b;
231  else if constexpr (descriptor<B>) return a == std::array {b};
232  else if constexpr (fixed_pattern<A> and fixed_pattern<B>) return detail::compare_fixed(a, b) == detail::ordering::equivalent;
233  else return detail::compare_impl(a, b) == detail::ordering::equivalent;
234  }
235 
236 
237  template<typename A, typename B, std::enable_if_t<pattern<A> and pattern<B>, int> = 0>
238  constexpr bool operator<(const A& a, const B& b)
239  {
241  if (get_dimension(a) == 0) return true;
242 
243  if constexpr ((descriptor<A> and descriptor<B>) or descriptor<B>) return false;
244  else if constexpr (descriptor<A>) return std::array {a} < b;
245  else if constexpr (fixed_pattern<A> and fixed_pattern<B>) return detail::compare_fixed(a, b) == detail::ordering::less;
246  else return detail::compare_impl(a, b) == detail::ordering::less;
247  }
248 
249 
250  template<typename A, typename B, std::enable_if_t<pattern<A> and pattern<B>, int> = 0>
251  constexpr bool operator>(const A& a, const B& b)
252  {
254  if (get_dimension(b) == 0) return true;
255 
256  if constexpr ((descriptor<A> and descriptor<B>) or descriptor<A>) return false;
257  else if constexpr (descriptor<B>) return a > std::array {b};
258  else if constexpr (fixed_pattern<A> and fixed_pattern<B>) return detail::compare_fixed(a, b) == detail::ordering::greater;
259  else return detail::compare_impl(a, b) == detail::ordering::greater;
260  }
261 
262 
263  template<typename A, typename B, std::enable_if_t<pattern<A> and pattern<B>, int> = 0>
264  constexpr bool operator<=(const A& a, const B& b)
265  {
267  if (get_dimension(a) == 0) return true;
268 
269  if constexpr (descriptor<A> and descriptor<B>) return internal::get_hash_code(a) == internal::get_hash_code(b);
270  else if constexpr (descriptor<A>) return std::array {a} <= b;
271  else if constexpr (descriptor<B>) return a == std::array {b};
272  else if constexpr (fixed_pattern<A> and fixed_pattern<B>)
273  { auto comp = detail::compare_fixed(a, b); return comp == detail::ordering::less or comp == detail::ordering::equivalent; }
274  else
275  { auto comp = detail::compare_impl(a, b); return comp == detail::ordering::less or comp == detail::ordering::equivalent; }
276  }
277 
278 
279  template<typename A, typename B, std::enable_if_t<pattern<A> and pattern<B>, int> = 0>
280  constexpr bool operator>=(const A& a, const B& b)
281  {
283  if (get_dimension(b) == 0) return true;
284 
285  if constexpr (descriptor<A> and descriptor<B>) return internal::get_hash_code(a) == internal::get_hash_code(b);
286  else if constexpr (descriptor<B>) return a >= std::array {b};
287  else if constexpr (descriptor<A>) return std::array {a} == b;
288  else if constexpr (fixed_pattern<A> and fixed_pattern<B>)
289  { auto comp = detail::compare_fixed(a, b); return comp == detail::ordering::greater or comp == detail::ordering::equivalent; }
290  else
291  { auto comp = detail::compare_impl(a, b); return comp == detail::ordering::greater or comp == detail::ordering::equivalent; }
292  }
293 
294 
295  template<typename A, typename B, std::enable_if_t<pattern<A> and pattern<B>, int> = 0>
296  constexpr bool operator!=(const A& a, const B& b)
297  {
298  return not operator==(a, b);
299  }
300 #endif
301 
302 
303 } // namespace OpenKalman::coordinates
304 
305 
306 #endif //OPENKALMAN_COORDINATES_COMPARISON_OPERATORS_HPP
Definition for coordinates::euclidean_pattern.
Definition for get_hash_code.
A generalization of std::greater in which the arguments may be of different types.
Definition: greater.hpp:29
Definition for values::to_number.
A generalization of std::less in which the arguments may be of different types.
Definition: less.hpp:29
Definition: comparison-operators.hpp:54
constexpr auto to_number(Arg arg)
Convert any values::value to a values::number.
Definition: to_number.hpp:34
constexpr auto get_dimension(const Arg &arg)
Get the vector dimension of coordinates::pattern Arg.
Definition: get_dimension.hpp:55
Definition for coordinates::descriptor.
Definition: compares_with.hpp:28
constexpr auto get_is_euclidean(const Arg &arg)
Determine, whether coordinates::pattern Arg is euclidean.
Definition: get_is_euclidean.hpp:70
Definition for coordinates::get_dimension.
Global definitions for OpenKalman.
constexpr auto get_size(Arg &&arg)
Get the size of a sized object (e.g, a collection)
Definition: get_size.hpp:191
A matrix with typed rows and columns.
Definition: forward-class-declarations.hpp:448