EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
Loading...
Searching...
No Matches
Discrete.h
Go to the documentation of this file.
1#pragma once
2
9
10#include <array>
11#include <concepts>
12#include <cstddef>
13#include <tuple>
14#include <type_traits>
15#include <utility>
16
21
22namespace Emrys
23{
24
62
71template <typename S>
73 requires {
74 typename std::integral_constant<int, S::Cardinality>;
75 typename std::integral_constant<int, S::MaxCardinality>;
76 } && (S::Cardinality == Eigen::Dynamic || (S::Cardinality >= 1 && S::MaxCardinality == S::Cardinality))
77 && std::regular<S> && requires(S const & space) {
78 { space.cardinality() } -> std::convertible_to<Eigen::Index>;
79 };
80
81namespace internal
82{
83
85template <int Cardinality, int MaxCardinality>
86class FiniteExtent
87{
88public:
89
91 [[nodiscard]] static constexpr Eigen::Index
92 cardinality()
93 {
94 return Cardinality;
95 }
96
98 friend bool
99 operator==(FiniteExtent const &, FiniteExtent const &) = default;
100};
101
104template <int MaxCardinality>
105class FiniteExtent<Eigen::Dynamic, MaxCardinality>
106{
107public:
108
110 FiniteExtent() = default;
111
113 constexpr explicit FiniteExtent(Eigen::Index n)
114 : m_cardinality{n}
115 {
116 EMRYS_FILTER_ASSERT(
117 n >= 1 && (MaxCardinality == Eigen::Dynamic || n <= MaxCardinality)
118 && "the cardinality does not fit the space"
119 );
120 }
121
123 [[nodiscard]] constexpr Eigen::Index
124 cardinality() const
125 {
126 return m_cardinality;
127 }
128
130 friend bool
131 operator==(FiniteExtent const &, FiniteExtent const &) = default;
132
133private:
134
135 Eigen::Index m_cardinality = 1;
136};
137
139template <int... Cardinalities>
140consteval int
141productCardinality()
142{
143 int result = 1;
144 ((result = result == Eigen::Dynamic || Cardinalities == Eigen::Dynamic ? Eigen::Dynamic : result * Cardinalities),
145 ...);
146 return result;
147}
148
149} // namespace internal
150
160template <int Cardinality_, int MaxCardinality_ = Cardinality_>
161struct Finite : internal::FiniteExtent<Cardinality_, MaxCardinality_>
162{
163 static_assert(Cardinality_ == Eigen::Dynamic || Cardinality_ >= 1, "a discrete space has at least one value");
164
165 static_assert(
166 Cardinality_ == Eigen::Dynamic || MaxCardinality_ == Cardinality_,
167 "a fixed cardinality is its own maximum"
168 );
169
170 static_assert(MaxCardinality_ == Eigen::Dynamic || MaxCardinality_ >= 1);
171 static constexpr int Cardinality = Cardinality_;
172 static constexpr int MaxCardinality = MaxCardinality_;
173
174 Finite() = default;
175
177 constexpr explicit Finite(Eigen::Index n)
178 requires(Cardinality_ == Eigen::Dynamic)
179 : internal::FiniteExtent<Cardinality_, MaxCardinality_>(n)
180 {}
181
183 friend bool
184 operator==(Finite const &, Finite const &) = default;
185};
186
193template <typename... Values>
194struct Named
195{
196 static_assert(sizeof...(Values) >= 1, "a discrete space has at least one value");
197 static constexpr int Cardinality = int(sizeof...(Values));
198 static constexpr int MaxCardinality = Cardinality;
199
201 template <typename Value>
202 static constexpr bool contains = (std::is_same_v<Value, Values> || ...);
203
204private:
205
207 template <typename Value>
208 static constexpr std::size_t occurrences = (std::size_t{0} + ... + std::size_t{std::is_same_v<Value, Values>});
209
210 static_assert(((occurrences<Values> == 1) && ...), "the values of a Named space must be distinct");
211
212public:
213
215 [[nodiscard]] static constexpr Eigen::Index
217 {
218 return Cardinality;
219 }
220
222 template <typename Value>
223 [[nodiscard]] static constexpr Eigen::Index
225 {
226 static_assert(contains<Value>, "the space has no value with this name");
227 Eigen::Index result = 0;
228 bool found = false;
229 ((found = found || std::is_same_v<Value, Values>, result += found ? 0 : 1), ...);
230 return result;
231 }
232
234 friend bool
235 operator==(Named const &, Named const &) = default;
236};
237
245template <DiscreteSpace... Axes>
246class DiscreteProduct : private internal::PartStorage<Axes...>
247{
248 static_assert(sizeof...(Axes) > 0, "a product needs at least one axis");
249
251 using Storage = internal::PartStorage<Axes...>;
252
253public:
254
255 static constexpr std::size_t AxisCount = sizeof...(Axes);
256
258 static constexpr int Cardinality = internal::productCardinality<Axes::Cardinality...>();
259
261 static constexpr int MaxCardinality = internal::productCardinality<Axes::MaxCardinality...>();
262
263 DiscreteProduct() = default;
264
266 constexpr explicit DiscreteProduct(Axes... axes)
267 : Storage{std::move(axes)...}
268 {}
269
271 template <std::size_t I>
272 [[nodiscard]] constexpr auto const &
273 axis() const
274 {
275 return Storage::template part<I>();
276 }
277
279 [[nodiscard]] constexpr Eigen::Index
281 {
282 return [&]<std::size_t... I>(std::index_sequence<I...>) {
283 return (Eigen::Index{1} * ... * axis<I>().cardinality());
284 }(std::make_index_sequence<AxisCount>{});
285 }
286
288 template <std::size_t I>
289 [[nodiscard]] constexpr Eigen::Index
290 stride() const
291 {
292 return [&]<std::size_t... J>(std::index_sequence<J...>) {
293 return (Eigen::Index{1} * ... * axis<I + 1 + J>().cardinality());
294 }(std::make_index_sequence<AxisCount - 1 - I>{});
295 }
296
298 [[nodiscard]] constexpr Eigen::Index
299 value(std::array<Eigen::Index, AxisCount> const & coordinates) const
300 {
301 auto const cardinalities = [&]<std::size_t... I>(std::index_sequence<I...>) {
302 return std::array<Eigen::Index, AxisCount>{axis<I>().cardinality()...};
303 }(std::make_index_sequence<AxisCount>{});
304 Eigen::Index result = 0;
305 for (std::size_t i = 0; i < AxisCount; ++i)
306 {
307 EMRYS_FILTER_ASSERT(
308 coordinates[i] >= 0 && coordinates[i] < cardinalities[i] && "a coordinate is out of range"
309 );
310 result = result * cardinalities[i] + coordinates[i];
311 }
312 return result;
313 }
314
316 [[nodiscard]] constexpr std::array<Eigen::Index, AxisCount>
317 coordinates(Eigen::Index value) const
318 {
319 EMRYS_FILTER_ASSERT(value >= 0 && value < cardinality() && "the value is out of range");
320 auto result = std::array<Eigen::Index, AxisCount>{};
321 [&]<std::size_t... I>(std::index_sequence<I...>) {
322 ((result[I] = (value / stride<I>()) % axis<I>().cardinality()), ...);
323 }(std::make_index_sequence<AxisCount>{});
324 return result;
325 }
326
328 friend bool
329 operator==(DiscreteProduct const &, DiscreteProduct const &) = default;
330};
331
332namespace internal
333{
334
336template <typename... Axes>
337std::tuple<Axes...> *
338discreteProductAxes(DiscreteProduct<Axes...> const *);
339
341template <typename T>
342concept IsDiscreteProduct = requires(T const * t) { discreteProductAxes(t); };
343
345template <typename T>
346using DiscreteProductAxes = std::remove_pointer_t<decltype(discreteProductAxes(std::declval<T const *>()))>;
347
349template <typename... Values>
350std::true_type
351namedValues(Named<Values...> const *);
352
354std::false_type
355namedValues(void const *);
356
358template <typename T>
359concept IsNamed = decltype(namedValues(std::declval<T const *>()))::value;
360
362template <typename T>
363inline constexpr bool IsUntaggedAxis = false;
365template <int Cardinality, int MaxCardinality>
366inline constexpr bool IsUntaggedAxis<Finite<Cardinality, MaxCardinality>>{true};
367
368template <typename... Values>
369inline constexpr bool IsUntaggedAxis<Named<Values...>>{true};
370
371template <typename... Axes>
372inline constexpr bool IsUntaggedAxis<DiscreteProduct<Axes...>>{true};
373
375
377template <typename Tag, typename S>
378consteval int
379countAxis()
380{
381 if constexpr (std::is_same_v<Tag, S>)
382 {
383 return 1;
384 }
385 else if constexpr (IsDiscreteProduct<S>)
386 {
387 return []<typename... Axes>(std::tuple<Axes...> *) {
388 return (countAxis<Tag, Axes>() + ... + 0);
389 }(static_cast<DiscreteProductAxes<S> *>(nullptr));
390 }
391 else
392 {
393 return 0;
394 }
395}
396
398template <typename Value, typename S>
399consteval int
400countValue()
401{
402 if constexpr (IsNamed<S>)
403 {
404 return S::template contains<Value> ? 1 : 0;
405 }
406 else if constexpr (IsDiscreteProduct<S>)
407 {
408 return []<typename... Axes>(std::tuple<Axes...> *) {
409 return (countValue<Value, Axes>() + ... + 0);
410 }(static_cast<DiscreteProductAxes<S> *>(nullptr));
411 }
412 else
413 {
414 return 0;
415 }
416}
417
419template <typename S, typename F>
420constexpr void
421forEachAxis(S const & space, F && f)
422{
423 [&]<std::size_t... I>(std::index_sequence<I...>) {
424 (f(space.template axis<I>(), space.template stride<I>()), ...);
425 }(std::make_index_sequence<std::tuple_size_v<DiscreteProductAxes<S>>>{});
426}
427
429template <typename Tag, typename S>
430[[nodiscard]] constexpr std::pair<Eigen::Index, Tag const *>
431locateAxis(S const & space)
432{
433 if constexpr (std::is_same_v<Tag, S>)
434 {
435 return {1, &space};
436 }
437 else
438 {
439 static_assert(
440 !IsUntaggedAxis<Tag>,
441 "a path names axes by tag; derive one, such as struct Maneuver : Named<Cruise, Turn> {}"
442 );
443 static_assert(countAxis<Tag, S>() > 0, "the space does not have this axis");
444 static_assert(countAxis<Tag, S>() < 2, "the axis appears more than once; qualify it with an enclosing tag");
445 auto found = std::pair<Eigen::Index, Tag const *>{0, nullptr};
446 forEachAxis(space, [&]<typename Axis>(Axis const & axis, Eigen::Index stride) {
447 if constexpr (countAxis<Tag, Axis>() > 0)
448 {
449 auto const inner = locateAxis<Tag>(axis);
450 found = {inner.first * stride, inner.second};
451 }
452 });
453 return found;
454 }
455}
456
458template <typename Tag, typename... Rest, typename S>
459[[nodiscard]] constexpr auto
460locateAxisPath(S const & space)
461{
462 auto const [stride, tag] = locateAxis<Tag>(space);
463 if constexpr (sizeof...(Rest) == 0)
464 {
465 return std::pair{stride, tag};
466 }
467 else
468 {
469 auto const [innerStride, inner] = locateAxisPath<Rest...>(*tag);
470 return std::pair{stride * innerStride, inner};
471 }
472}
473
477struct ValueLocation
478{
479 Eigen::Index stride;
480 Eigen::Index cardinality;
481 Eigen::Index index;
482
484 [[nodiscard]] constexpr bool
485 matches(Eigen::Index value) const
486 {
487 return (value / stride) % cardinality == index;
488 }
489};
490
492template <typename Value, typename S>
493[[nodiscard]] constexpr ValueLocation
494locateValue(S const & space)
495{
496 static_assert(countValue<Value, S>() > 0, "the space has no value with this name");
497 static_assert(
498 countValue<Value, S>() < 2,
499 "the value names a value of more than one axis; qualify it with a TagPath to its axis"
500 );
501 if constexpr (IsNamed<S>)
502 {
503 return {1, space.cardinality(), S::template index<Value>()};
504 }
505 else
506 {
507 auto found = ValueLocation{};
508 forEachAxis(space, [&]<typename Axis>(Axis const & axis, Eigen::Index stride) {
509 if constexpr (countValue<Value, Axis>() > 0)
510 {
511 ValueLocation const inner = locateValue<Value>(axis);
512 found = {inner.stride * stride, inner.cardinality, inner.index};
513 }
514 });
515 return found;
516 }
517}
518
520template <typename P, typename S>
521[[nodiscard]] constexpr ValueLocation
522locateValuePath(S const & space)
523{
524 return withPathTags<P>([&]<typename... Tags>() {
525 constexpr std::size_t count = sizeof...(Tags);
526 using Value = std::tuple_element_t<count - 1, std::tuple<Tags...>>;
527 if constexpr (count == 1)
528 {
529 return locateValue<Value>(space);
530 }
531 else
532 {
533 return [&]<std::size_t... I>(std::index_sequence<I...>) {
534 auto const [stride, axis] = locateAxisPath<std::tuple_element_t<I, std::tuple<Tags...>>...>(space);
535 ValueLocation inner = locateValue<Value>(*axis);
536 inner.stride *= stride;
537 return inner;
538 }(std::make_index_sequence<count - 1>{});
539 }
540 });
541}
542
544template <typename P, typename S>
545[[nodiscard]] constexpr auto
546locateAxisByPath(S const & space)
547{
548 return withPathTags<P>([&]<typename... Tags>() {
549 return locateAxisPath<Tags...>(space);
550 });
551}
552
554template <typename... Axes>
555[[nodiscard]] auto
556joinAxes(std::tuple<Axes...> axes)
557{
558 static_assert(sizeof...(Axes) > 0, "nothing is left of the space");
559 if constexpr (sizeof...(Axes) == 1)
560 {
561 return std::get<0>(std::move(axes));
562 }
563 else
564 {
565 return std::apply(
566 [](Axes &... d) {
567 return DiscreteProduct<Axes...>{std::move(d)...};
568 },
569 axes
570 );
571 }
572}
573
574template <typename Tag, typename... Rest, typename S>
575[[nodiscard]] auto
576axesWithout(S const & space); // defined below; axisWithout() recurses into it
577
579template <typename Tag, typename... Rest, typename Axis>
580[[nodiscard]] auto
581axisWithout(Axis const & axis)
582{
583 if constexpr (countAxis<Tag, Axis>() > 0)
584 {
585 return axesWithout<Tag, Rest...>(axis);
586 }
587 else
588 {
589 return std::tuple<Axis>{axis};
590 }
591}
592
597template <typename Tag, typename... Rest, typename S>
598[[nodiscard]] auto
599axesWithout(S const & space)
600{
601 if constexpr (std::is_same_v<Tag, S>)
602 {
603 if constexpr (sizeof...(Rest) == 0)
604 {
605 return std::tuple<>{};
606 }
607 else
608 {
609 return axesWithout<Rest...>(space);
610 }
611 }
612 else
613 {
614 static_assert(
615 !IsUntaggedAxis<Tag>,
616 "a path names axes by tag; derive one, such as struct Maneuver : Named<Cruise, Turn> {}"
617 );
618 static_assert(IsDiscreteProduct<S>, "the space does not have this axis");
619 return [&]<std::size_t... I>(std::index_sequence<I...>) {
620 return std::tuple_cat(axisWithout<Tag, Rest...>(space.template axis<I>())...);
621 }(std::make_index_sequence<std::tuple_size_v<DiscreteProductAxes<S>>>{});
622 }
623}
624
626template <typename P, typename S>
627[[nodiscard]] auto
628complementAxes(S const & space)
629{
630 return withPathTags<P>([&]<typename... Tags>() {
631 return joinAxes(axesWithout<Tags...>(space));
632 });
633}
634
635template <typename Value, typename S>
636struct ValueAxisOf;
637
639template <typename Value, typename Axes>
640struct ValueAxisIn
641{
643 using type = void;
644};
645
647template <typename Value, typename Axis, typename... Rest>
648struct ValueAxisIn<Value, std::tuple<Axis, Rest...>>
649{
651 using type = typename std::conditional_t<
652 (countValue<Value, Axis>() > 0),
653 ValueAxisOf<Value, Axis>,
654 ValueAxisIn<Value, std::tuple<Rest...>>>::type;
655};
656
658template <typename Value, typename S>
659struct ValueAxisOf
660{
662 using type = typename ValueAxisIn<Value, DiscreteProductAxes<S>>::type;
663};
664
666template <typename Value, typename S>
667 requires IsNamed<S>
668struct ValueAxisOf<Value, S>
669{
671 using type = S;
672};
673
675template <typename Value, typename S>
676using ValueAxis = typename ValueAxisOf<Value, S>::type;
677
679template <typename P, typename S>
680struct ValueAxisPathOf
681{
683 using type = TagPath<ValueAxis<P, S>>;
684};
685
689template <typename... Tags, typename S>
690struct ValueAxisPathOf<TagPath<Tags...>, S>
691{
692 static constexpr std::size_t Count = sizeof...(Tags);
693
695 using Value = std::tuple_element_t<Count - 1, std::tuple<Tags...>>;
696
698 template <std::size_t... I>
699 static consteval auto
700 make(std::index_sequence<I...>)
701 {
702 if constexpr (Count == 1)
703 {
704 return TagPath<ValueAxis<Value, S>>{};
705 }
706 else
707 {
708 using Last = std::tuple_element_t<Count - 2, std::tuple<Tags...>>;
709 return TagPath<std::tuple_element_t<I, std::tuple<Tags...>>..., ValueAxis<Value, Last>>{};
710 }
711 }
712
714 using type = decltype(make(std::make_index_sequence<Count - 1>{}));
715};
716
720template <typename P, typename S>
721using ValueAxisPath = typename ValueAxisPathOf<P, S>::type;
722
724template <typename S>
725consteval int
726leafAxisCount()
727{
728 if constexpr (IsDiscreteProduct<S>)
729 {
730 return []<typename... Axes>(std::tuple<Axes...> *) {
731 return (leafAxisCount<Axes>() + ... + 0);
732 }(static_cast<DiscreteProductAxes<S> *>(nullptr));
733 }
734 else
735 {
736 return 1;
737 }
738}
739
741struct LeafRange
742{
743 int first = 0;
744 int count = 0;
745};
746
748template <typename Tag, typename S>
749consteval LeafRange
750leafRangeOfAxis()
751{
752 if constexpr (std::is_same_v<Tag, S>)
753 {
754 return {0, leafAxisCount<S>()};
755 }
756 else if constexpr (IsDiscreteProduct<S>)
757 {
758 return []<typename... Axes>(std::tuple<Axes...> *) {
759 LeafRange result{};
760 int offset = 0;
761 auto const visit = [&]<typename Axis>() {
762 if constexpr (countAxis<Tag, Axis>() > 0)
763 {
764 LeafRange const inner = leafRangeOfAxis<Tag, Axis>();
765 result = {offset + inner.first, inner.count};
766 }
767 offset += leafAxisCount<Axis>();
768 };
769 (visit.template operator()<Axes>(), ...);
770 return result;
771 }(static_cast<DiscreteProductAxes<S> *>(nullptr));
772 }
773 else
774 {
775 return {};
776 }
777}
778
780template <typename S, typename Tag, typename... Rest>
781consteval LeafRange
782leafRangeOfAxisPath()
783{
784 LeafRange const outer = leafRangeOfAxis<Tag, S>();
785 if constexpr (sizeof...(Rest) == 0)
786 {
787 return outer;
788 }
789 else
790 {
791 LeafRange const inner = leafRangeOfAxisPath<Tag, Rest...>();
792 return {outer.first + inner.first, inner.count};
793 }
794}
795
797template <typename P, typename S>
798consteval LeafRange
799leafRangeOfPath()
800{
801 return []<typename... Tags>(TagPath<Tags...> *) {
802 return leafRangeOfAxisPath<S, Tags...>();
803 }(static_cast<AsTagPath<P> *>(nullptr));
804}
805
807template <typename Value, typename S>
808consteval int
809leafOfValue()
810{
811 if constexpr (IsDiscreteProduct<S>)
812 {
813 return []<typename... Axes>(std::tuple<Axes...> *) {
814 int result = 0;
815 int offset = 0;
816 auto const visit = [&]<typename Axis>() {
817 if constexpr (countValue<Value, Axis>() > 0)
818 {
819 result = offset + leafOfValue<Value, Axis>();
820 }
821 offset += leafAxisCount<Axis>();
822 };
823 (visit.template operator()<Axes>(), ...);
824 return result;
825 }(static_cast<DiscreteProductAxes<S> *>(nullptr));
826 }
827 else
828 {
829 return 0;
830 }
831}
832
834template <typename P, typename S>
835consteval int
836leafOfValuePath()
837{
838 return []<typename... Tags>(TagPath<Tags...> *) {
839 constexpr std::size_t count = sizeof...(Tags);
840 using Value = std::tuple_element_t<count - 1, std::tuple<Tags...>>;
841 if constexpr (count == 1)
842 {
843 return leafOfValue<Value, S>();
844 }
845 else
846 {
847 return []<std::size_t... I>(std::index_sequence<I...>) {
848 using Axis = std::tuple_element_t<count - 2, std::tuple<Tags...>>;
849 return leafRangeOfAxisPath<S, std::tuple_element_t<I, std::tuple<Tags...>>...>().first
850 + leafOfValue<Value, Axis>();
851 }(std::make_index_sequence<count - 1>{});
852 }
853 }(static_cast<AsTagPath<P> *>(nullptr));
854}
855
857template <typename S, typename Tag, typename... Rest>
858consteval bool
859axisPathResolves()
860{
861 if constexpr (!std::is_same_v<Tag, S> && (IsUntaggedAxis<Tag> || countAxis<Tag, S>() != 1))
862 {
863 return false;
864 }
865 else if constexpr (sizeof...(Rest) == 0)
866 {
867 return true;
868 }
869 else
870 {
871 return axisPathResolves<Tag, Rest...>();
872 }
873}
874
876template <typename P, typename S>
877consteval bool
878pathResolves()
879{
880 return []<typename... Tags>(TagPath<Tags...> *) {
881 return axisPathResolves<S, Tags...>();
882 }(static_cast<AsTagPath<P> *>(nullptr));
883}
884
886template <typename P, typename S>
887consteval bool
888valuePathResolves()
889{
890 return []<typename... Tags>(TagPath<Tags...> *) {
891 constexpr std::size_t count = sizeof...(Tags);
892 using Value = std::tuple_element_t<count - 1, std::tuple<Tags...>>;
893 if constexpr (count == 1)
894 {
895 return countValue<Value, S>() == 1;
896 }
897 else
898 {
899 return []<std::size_t... I>(std::index_sequence<I...>) {
900 using Axis = std::tuple_element_t<count - 2, std::tuple<Tags...>>;
901 if constexpr (!axisPathResolves<S, std::tuple_element_t<I, std::tuple<Tags...>>...>())
902 {
903 return false;
904 }
905 else
906 {
907 return countValue<Value, Axis>() == 1;
908 }
909 }(std::make_index_sequence<count - 1>{});
910 }
911 }(static_cast<AsTagPath<P> *>(nullptr));
912}
913
915template <typename S>
916consteval std::array<bool, std::size_t(leafAxisCount<S>())>
917namedLeaves()
918{
919 auto result = std::array<bool, std::size_t(leafAxisCount<S>())>{};
920 if constexpr (IsDiscreteProduct<S>)
921 {
922 [&]<typename... Axes>(std::tuple<Axes...> *) {
923 std::size_t offset = 0;
924 auto const append = [&]<typename Axis>() {
925 for (bool const named : namedLeaves<Axis>())
926 {
927 result[offset++] = named;
928 }
929 };
930 (append.template operator()<Axes>(), ...);
931 }(static_cast<DiscreteProductAxes<S> *>(nullptr));
932 }
933 else
934 {
935 result[0] = IsNamed<S>;
936 }
937 return result;
938}
939
941enum class Naming
942{
943 complete,
944 unresolved,
945 repeated,
946 missingNamed,
947 missingUnnamed,
948};
949
951template <std::size_t N>
952consteval void
953countLeaves(std::array<int, N> & times, LeafRange range)
954{
955 for (int k = range.first; k < range.first + range.count; ++k)
956 {
957 ++times[std::size_t(k)];
958 }
959}
960
964template <typename S, typename... Values, typename... Paths>
965consteval Naming
966namingOf(std::tuple<Values...> *, std::tuple<Paths...> *)
967{
968 if constexpr (!((valuePathResolves<Values, S>() && ...) && (pathResolves<Paths, S>() && ...)))
969 {
970 return Naming::unresolved;
971 }
972 else
973 {
974 auto times = std::array<int, std::size_t(leafAxisCount<S>())>{};
975 (countLeaves(times, LeafRange{leafOfValuePath<Values, S>(), 1}), ...);
976 (countLeaves(times, leafRangeOfPath<Paths, S>()), ...);
977 constexpr auto named = namedLeaves<S>();
978 for (int const n : times)
979 {
980 if (n > 1)
981 {
982 return Naming::repeated;
983 }
984 }
985 for (std::size_t k = 0; k < times.size(); ++k)
986 {
987 if (times[k] == 0 && named[k])
988 {
989 return Naming::missingNamed;
990 }
991 }
992 for (int const n : times)
993 {
994 if (n == 0)
995 {
996 return Naming::missingUnnamed;
997 }
998 }
999 return Naming::complete;
1000 }
1001}
1002
1004template <typename P>
1005struct LastTagOf;
1006
1008template <typename... Tags>
1009struct LastTagOf<TagPath<Tags...>>
1010{
1012 using type = std::tuple_element_t<sizeof...(Tags) - 1, std::tuple<Tags...>>;
1013};
1014
1016template <typename P>
1017using LastTag = typename LastTagOf<AsTagPath<P>>::type;
1018
1020template <typename S>
1021[[nodiscard]] constexpr std::array<Eigen::Index, std::size_t(leafAxisCount<S>())>
1022leafCardinalities(S const & space)
1023{
1024 auto result = std::array<Eigen::Index, std::size_t(leafAxisCount<S>())>{};
1025 if constexpr (IsDiscreteProduct<S>)
1026 {
1027 std::size_t offset = 0;
1028 forEachAxis(space, [&]<typename Axis>(Axis const & axis, Eigen::Index) {
1029 for (Eigen::Index const cardinality : leafCardinalities(axis))
1030 {
1031 result[offset++] = cardinality;
1032 }
1033 });
1034 }
1035 else
1036 {
1037 result[0] = space.cardinality();
1038 }
1039 return result;
1040}
1041
1043template <typename P, typename S>
1044[[nodiscard]] constexpr Eigen::Index
1045namedOffset(S const & space)
1046{
1047 ValueLocation const location = locateValuePath<P>(space);
1048 return location.index * location.stride;
1049}
1050
1051} // namespace internal
1052
1057template <typename Path, std::size_t N>
1059{
1060 std::array<Eigen::Index, N> indices;
1061};
1062
1069template <typename Path, std::integral... I>
1070[[nodiscard]] constexpr AxisIndex<Path, sizeof...(I)>
1071at(I... indices)
1072{
1073 using Axis = internal::LastTag<Path>;
1074 static_assert(DiscreteSpace<Axis>, "at<Axis>() names an axis of a discrete space");
1075 static_assert(
1076 sizeof...(I) == std::size_t(internal::leafAxisCount<Axis>()),
1077 "give one index for each leaf axis of the axis named"
1078 );
1079 return {{Eigen::Index(indices)...}};
1080}
1081
1082namespace internal
1083{
1084
1086template <typename Path, std::size_t N, typename S>
1087[[nodiscard]] constexpr Eigen::Index
1088indexedOffset(S const & space, AxisIndex<Path, N> const & index)
1089{
1090 auto const [stride, axis] = locateAxisByPath<Path>(space);
1091 auto const cardinalities = leafCardinalities(*axis);
1092 Eigen::Index flat = 0;
1093 for (std::size_t k = 0; k < N; ++k)
1094 {
1095 EMRYS_FILTER_ASSERT(index.indices[k] >= 0 && index.indices[k] < cardinalities[k] && "an index is out of range");
1096 flat = flat * cardinalities[k] + index.indices[k];
1097 }
1098 return flat * stride;
1099}
1100
1101} // namespace internal
1102
1110template <typename... Values, DiscreteSpace S, typename... Paths, std::size_t... N>
1111[[nodiscard]] constexpr Eigen::Index
1112valueOf(S const & space, AxisIndex<Paths, N> const &... indices)
1113{
1114 constexpr bool named = sizeof...(Values) + sizeof...(Paths) > 0;
1115 static_assert(named, "name a value");
1116 constexpr internal::Naming naming = !named ? internal::Naming::unresolved
1117 : internal::namingOf<S>(
1118 static_cast<std::tuple<Values...> *>(nullptr),
1119 static_cast<std::tuple<Paths...> *>(nullptr)
1120 );
1121 static_assert(naming != internal::Naming::repeated, "an axis is named more than once");
1122 static_assert(naming != internal::Naming::missingNamed, "name one value in each axis");
1123 static_assert(naming != internal::Naming::missingUnnamed, "an axis without names takes its index from at<Axis>(i)");
1124 return (Eigen::Index{0} + ... + internal::namedOffset<Values>(space))
1125 + (Eigen::Index{0} + ... + internal::indexedOffset(space, indices));
1126}
1127
1132template <DiscreteSpace S, typename... Values, typename... Paths, std::size_t... N>
1133 requires FixedSizeSpace<S>
1134[[nodiscard]] constexpr Eigen::Index
1135valueOf(AxisIndex<Paths, N> const &... indices)
1136{
1137 return valueOf<Values...>(S{}, indices...);
1138}
1139
1140} // namespace Emrys
Spaces built from other spaces (decisions.md [sizes-and-dimensions], [several-resizable-blocks]): Com...
The macros every header includes first.
The Space concept and what every continuous space shares (decisions.md [sizes-and-dimensions]): the t...
Tag paths and the index utilities (decisions.md [tag-addressing], [index-names]): TagPath and TagPath...
The product of the discrete spaces Axes: a value has one value in each axis.
Definition Discrete.h:247
static constexpr std::size_t AxisCount
the number of axes
Definition Discrete.h:255
constexpr std::array< Eigen::Index, AxisCount > coordinates(Eigen::Index value) const
Definition Discrete.h:317
constexpr Eigen::Index cardinality() const
Definition Discrete.h:280
constexpr Eigen::Index stride() const
Definition Discrete.h:290
static constexpr int MaxCardinality
the product of the axes' bounds, or Eigen::Dynamic if any is unbounded
Definition Discrete.h:261
constexpr Eigen::Index value(std::array< Eigen::Index, AxisCount > const &coordinates) const
Definition Discrete.h:299
constexpr DiscreteProduct(Axes... axes)
The product of axes, with their cardinalities.
Definition Discrete.h:266
constexpr auto const & axis() const
Definition Discrete.h:273
static constexpr int Cardinality
the product of the axes' cardinalities, or Eigen::Dynamic if any is
Definition Discrete.h:258
friend bool operator==(DiscreteProduct const &, DiscreteProduct const &)=default
Equal when every axis is.
Axes: a way of taking a root of a covariance , , whose columns carry a sigma-point rule's unit point...
Definition Roots.h:300
A type usable as a discrete space: a finite set of values.
Definition Discrete.h:72
constexpr AxisIndex< Path, sizeof...(I)> at(I... indices)
Definition Discrete.h:1071
constexpr Eigen::Index valueOf(S const &space, AxisIndex< Paths, N > const &... indices)
Definition Discrete.h:1112
An index in each leaf axis of the axis named by the path Path (a tag or a TagPath),...
Definition Discrete.h:1059
std::array< Eigen::Index, N > indices
one per leaf axis of the one named, in flat-index order
Definition Discrete.h:1060
constexpr Finite(Eigen::Index n)
n values of a run-time Finite.
Definition Discrete.h:177
friend bool operator==(Finite const &, Finite const &)=default
Equal when the cardinalities are; a fixed Finite is always equal.
static constexpr int MaxCardinality
a bound on it, or Eigen::Dynamic
Definition Discrete.h:172
static constexpr int Cardinality
the number of values, or Eigen::Dynamic
Definition Discrete.h:171
Finite()=default
A GridAxis is made like a Finite, with its number of cells.
Values named by the tag types Values, in order: struct Maneuver : Named<Cruise, Turn> {}...
Definition Discrete.h:195
static constexpr Eigen::Index index()
Definition Discrete.h:224
friend bool operator==(Named const &, Named const &)=default
Always equal: a Named has no state.
static constexpr int MaxCardinality
the same: a Named is fixed
Definition Discrete.h:198
static constexpr int Cardinality
the number of names
Definition Discrete.h:197
static constexpr bool contains
Whether Value names one of the values.
Definition Discrete.h:202
static constexpr Eigen::Index cardinality()
Definition Discrete.h:216