OpenKalman
compare_three_way.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) 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_COMPARE_THREE_WAY_HPP
17 #define OPENKALMAN_COORDINATES_COMPARE_THREE_WAY_HPP
18 
26 
28 {
29  namespace detail
30  {
31  template<std::size_t ia = 0, std::size_t ib = 0, typename A, typename B, typename C>
32  constexpr auto
33  compare_three_way_fixed(const A& a, const B& b, const C& c, std::size_t abank = 0, std::size_t bbank = 0)
34  {
35  if constexpr (ia < collections::size_of_v<A>)
36  {
37  auto ai = collections::get<ia>(a);
38  if constexpr (ib < collections::size_of_v<B>)
39  {
40  auto bi = collections::get<ib>(b);
41  constexpr bool ae = euclidean_pattern<decltype(ai)>;
42  constexpr bool be = euclidean_pattern<decltype(bi)>;
43  if constexpr (ae and be)
44  return compare_three_way_fixed<ia + 1, ib + 1>(a, b, c, abank + get_dimension(ai), bbank + get_dimension(bi));
45  else if constexpr (ae)
46  return compare_three_way_fixed<ia + 1, ib>(a, b, c, abank + get_dimension(ai), bbank);
47  else if constexpr (be)
48  return compare_three_way_fixed<ia, ib + 1>(a, b, c, abank, bbank + get_dimension(bi));
49  else
50  {
51  if (abank == bbank)
52  if (internal::get_descriptor_hash_code(ai) == internal::get_descriptor_hash_code(bi))
53  return values::cast_to<stdex::partial_ordering>(compare_three_way_fixed<ia + 1, ib + 1>(a, b, c));
54  return stdex::partial_ordering::unordered;
55  }
56  }
57  else if constexpr (euclidean_pattern<decltype(ai)>)
58  return compare_three_way_fixed<ia + 1, ib>(a, b, c, abank + get_dimension(ai), bbank);
59  else if (abank >= bbank)
60  return stdex::partial_ordering::greater;
61  else
62  return stdex::partial_ordering::unordered;
63  }
64  else if constexpr (ib < collections::size_of_v<B>) // ia >= collections::size_of_v<A>
65  {
66  auto bi = collections::get<ib>(b);
67  if constexpr (euclidean_pattern<decltype(bi)>) return compare_three_way_fixed<ia, ib + 1>(a, b, c, abank, bbank + get_dimension(bi));
68  else if (abank <= bbank) return stdex::partial_ordering::less;
69  else return stdex::partial_ordering::unordered;
70  }
71  else
72  {
73  return stdex::invoke(c, abank, bbank);
74  }
75  }
76 
77 
78  template<typename A, typename B, typename C, typename Ia = std::integral_constant<std::size_t, 0>, typename Ib = std::integral_constant<std::size_t, 0>>
79  constexpr stdex::partial_ordering
80  compare_three_way_impl(const A& a, const B& b, const C& c, Ia ia = {}, Ib ib = {}, std::size_t abank = 0, std::size_t bbank = 0)
81  {
82  if (ia < collections::get_size(a))
83  {
84  auto a_i = collections::get_element(a, ia);
85  auto ae = get_is_euclidean(a_i);
86  if (ib < collections::get_size(b))
87  {
88  auto b_i = collections::get_element(b, ib);
89  auto be = get_is_euclidean(b_i);
90  if (ae or be)
91  return compare_three_way_impl(a, b, c,
92  ae ? ia + 1_uz : ia,
93  be ? ib + 1_uz : ib,
94  ae ? abank + get_dimension(a_i) : abank,
95  be ? bbank + get_dimension(b_i) : bbank);
96  else if (internal::get_descriptor_hash_code(a_i) == internal::get_descriptor_hash_code(b_i) and abank == bbank)
97  return compare_three_way_impl(a, b, c, ia + 1_uz, ib + 1_uz);
98  else
99  return stdex::partial_ordering::unordered;
100  }
101  else
102  {
103  if (ae)
104  return compare_three_way_impl(a, b, c, ia + 1_uz, ib, abank + get_dimension(a_i), bbank);
105  else if (abank >= bbank)
106  return stdex::partial_ordering::greater;
107  else
108  return stdex::partial_ordering::unordered;
109  }
110  }
111  else if (ib < collections::get_size(b))
112  {
113  auto b_i = collections::get_element(b, ib);
114  if (get_is_euclidean(b_i))
115  return compare_three_way_impl(a, b, c, ia, ib + 1_uz, abank, bbank + get_dimension(b_i));
116  else if (abank <= bbank)
117  return stdex::partial_ordering::less;
118  else
119  return stdex::partial_ordering::unordered;
120  }
121  else return stdex::invoke(c, abank, bbank);
122  }
123 
124  }
125 
126 
132 #ifdef __cpp_concepts
133  template<pattern A, pattern B, typename Comparison = stdex::compare_three_way>
134  requires std::is_invocable_r_v<stdex::partial_ordering, Comparison, std::size_t, std::size_t>
135  constexpr std::convertible_to<stdex::partial_ordering> auto
136 #else
137  template<typename A, typename B, typename Comparison = stdex::compare_three_way,
138  std::enable_if_t<pattern<A> and pattern<B> and
140  constexpr auto
141 #endif
142  compare_three_way(A&& a, B&& b, const Comparison& c = {})
143  {
144  if constexpr (euclidean_pattern<A> and euclidean_pattern<B> and
145  (descriptor<A> or collections::sized<A>) and (descriptor<B> or collections::sized<B>))
146  {
147  return values::cast_to<stdex::partial_ordering>(values::operation(c, get_dimension(a), get_dimension(b)));
148  }
149 
150  else if constexpr (descriptor<A> and descriptor<B>)
151  {
152  if (get_is_euclidean(a) and get_is_euclidean(b))
154  else if (stdex::is_eq(stdex::invoke(c, internal::get_descriptor_hash_code(a), internal::get_descriptor_hash_code(b))))
155  return stdex::partial_ordering::equivalent;
156  else
157  return stdex::partial_ordering::unordered;
158  }
159  else if constexpr (descriptor<A>)
160  {
161  return compare_three_way(stdex::ranges::views::single(stdex::cref(a)), b, c);
162  }
163  else if constexpr (descriptor<B>)
164  {
165  return compare_three_way(a, stdex::ranges::views::single(stdex::cref(b)), c);
166  }
167 
168  else if constexpr (not collections::sized<A> and not collections::sized<B>)
169  {
170  using RA = stdex::ranges::range_value_t<A>;
171  using RB = stdex::ranges::range_value_t<B>;
172  // The situation where both RA and RB are zero at compile time is already handled in the euclidean case above
174  return compare_three_way(Dimensions<0>{}, b, c);
176  return compare_three_way(a, Dimensions<0>{}, c);
177  else if constexpr (values::fixed<decltype(internal::get_descriptor_hash_code(std::declval<RA>()))> and
178  values::fixed<decltype(internal::get_descriptor_hash_code(std::declval<RB>()))>)
179  {
180  auto cmp = values::cast_to<stdex::partial_ordering>(values::operation(
181  c,
182  values::fixed_value_of<decltype(internal::get_descriptor_hash_code(std::declval<RA>()))>{},
184  if constexpr (values::fixed_value_of_v<decltype(cmp)> == stdex::partial_ordering::less or
185  values::fixed_value_of_v<decltype(cmp)> == stdex::partial_ordering::greater)
187  else
188  return cmp;
189  }
190  else
191  {
193  }
194  }
195  else if constexpr (not collections::sized<A>)
196  {
197  using RA = stdex::ranges::range_value_t<A>;
199  {
200  return compare_three_way(Dimensions<0>{}, b, c);
201  }
202  else
203  {
204  auto offset = std::integral_constant<std::size_t, 0>{};
205  auto extent = values::operation(std::plus{}, collections::get_size(b), std::integral_constant<std::size_t, 1>{});
206  return compare_three_way(collections::views::slice(a, offset, extent), b, c);
207  }
208  }
209  else if constexpr (not collections::sized<B>)
210  {
211  using RB = stdex::ranges::range_value_t<B>;
213  {
214  return compare_three_way(a, Dimensions<0>{}, c);
215  }
216  else
217  {
218  auto offset = std::integral_constant<std::size_t, 0>{};
219  auto extent = values::operation(std::plus{}, collections::get_size(a), std::integral_constant<std::size_t, 1>{});
220  return compare_three_way(a, collections::views::slice(b, offset, extent), c);
221  }
222  }
223 
224  else if constexpr (collections::size_of_v<A> != stdex::dynamic_extent and collections::size_of_v<B> != stdex::dynamic_extent)
225  {
226  if constexpr (collections::size_of_v<A> == 0 or collections::size_of_v<B> == 0)
227  return values::cast_to<stdex::partial_ordering>(values::operation(c, collections::size_of<A>{}, collections::size_of<B>{}));
228  else
229  return detail::compare_three_way_fixed(a, b, c);
230  }
231  else if constexpr (collections::size_of_v<A> != stdex::dynamic_extent) // collections::size_of_v<B> == stdex::dynamic_extent
232  {
233  bool size_b_is_zero = values::to_value_type(collections::get_size(b)) == 0;
234  if constexpr (collections::size_of_v<A> == 0)
235  return size_b_is_zero ? stdex::partial_ordering::equivalent : stdex::partial_ordering::less;
236  else if (size_b_is_zero)
237  return stdex::partial_ordering::greater;
238  else
239  return detail::compare_three_way_impl(collections::views::all(std::forward<A>(a)), b, c);
240  }
241  else if constexpr (collections::size_of_v<B> != stdex::dynamic_extent) // collections::size_of_v<A> == stdex::dynamic_extent
242  {
243  bool size_a_is_zero = values::to_value_type(collections::get_size(a)) == 0;
244  if constexpr (collections::size_of_v<B> == 0)
245  return size_a_is_zero ? stdex::partial_ordering::equivalent : stdex::partial_ordering::greater;
246  else if (size_a_is_zero)
247  return stdex::partial_ordering::less;
248  else
249  return detail::compare_three_way_impl(a, collections::views::all(std::forward<B>(b)), c);
250  }
251  else
252  {
253  bool size_a_is_zero = values::to_value_type(collections::get_size(a)) == 0;
254  bool size_b_is_zero = values::to_value_type(collections::get_size(b)) == 0;
255  if (size_a_is_zero)
256  return size_b_is_zero ? stdex::partial_ordering::equivalent : stdex::partial_ordering::less;
257  else if (size_b_is_zero)
258  return stdex::partial_ordering::greater;
259  else
260  return detail::compare_three_way_impl(a, b, c);
261  }
262  }
263 
264 }
265 
266 #endif
Definition for coordinates::euclidean_pattern.
Definition: comparison.hpp:104
constexpr auto fixed_value_of_v
Helper template for fixed_value_of.
Definition: fixed_value_of.hpp:84
Definition of the Dimensions class.
decltype(auto) constexpr to_value_type(Arg &&arg)
Convert, if necessary, a fixed or dynamic value to its underlying base type.
Definition: to_value_type.hpp:28
The size of a sized object (including a collection).
Definition: size_of.hpp:33
The fixed value associated with a fixed.
Definition: fixed_value_of.hpp:44
constexpr bool value
T is a fixed or dynamic value that is reducible to a number.
Definition: value.hpp:45
Definition for coordinates::pattern.
The size of a coordinates::pattern.
Definition: dimension_of.hpp:36
constexpr auto get_dimension(const Arg &arg)
Get the vector dimension of coordinates::pattern Arg.
Definition: get_dimension.hpp:54
constexpr auto get_is_euclidean(const Arg &arg)
Determine, whether coordinates::pattern Arg is euclidean.
Definition: get_is_euclidean.hpp:65
Definition for coordinates::descriptor.
The namespace for features relating to coordinates::pattern object.
Definition: compares_with.hpp:25
Definition for get_descriptor_hash_code.
constexpr detail::all_closure all
a std::ranges::range_adaptor_closure which returns a view to all members of its collection argument...
Definition: all.hpp:72
Inclusion file for collections.
constexpr detail::slice_adapter slice
a RangeAdapterObject associated with slice_view.
Definition: slice.hpp:364
Definition for coordinates::get_dimension.
A structure representing the dimensions associated with of a particular index.
Definition: Dimensions.hpp:42
constexpr bool fixed_value_compares_with
T has a fixed value that compares with N in a particular way based on parameter comp.
Definition: fixed_value_compares_with.hpp:74
decltype(auto) constexpr get_element(Arg &&arg, I i)
A generalization of std::get and the range subscript operator.
Definition: get_element.hpp:122
constexpr auto compare_three_way(A &&a, B &&b, const Comparison &c={})
Compare two coordinates::pattern objects lexicographically.
Definition: compare_three_way.hpp:142
constexpr bool fixed
T is a value that is determinable at compile time.
Definition: fixed.hpp:66
A fixed version of std::partial_ordering::unordered.
Definition: fixed-constants.hpp:167
constexpr auto get_size(Arg &&arg)
Get the size of a sized object (e.g, a collection)
Definition: get_size.hpp:188
constexpr bool euclidean_pattern
A coordinates::pattern for a normal Euclidean vector.
Definition: euclidean_pattern.hpp:49
constexpr std::size_t size_of_v
Helper for collections::size_of.
Definition: size_of.hpp:60
constexpr auto operation(Operation &&op, Args &&...args)
A potentially constant-evaluated operation involving some number of values.
Definition: operation.hpp:98