refactor: compile-times optimized for the rest of the quantity_spec.h header file

This commit is contained in:
Mateusz Pusz
2024-06-13 17:15:27 +02:00
parent 921aae23dc
commit 5760d6e15c

View File

@@ -498,7 +498,7 @@ template<QuantitySpec Q>
#ifdef MP_UNITS_API_NO_CRTP #ifdef MP_UNITS_API_NO_CRTP
template<typename Q> template<typename Q>
requires detail::QuantitySpecWithNoSpecifiers<Q> && (detail::get_kind_tree_root(Q{}) == Q{}) requires detail::QuantitySpecWithNoSpecifiers<Q> && (decltype(detail::get_kind_tree_root(Q{})){} == Q{})
struct kind_of_<Q> final : Q::_base_type_ { struct kind_of_<Q> final : Q::_base_type_ {
using _base_type_ = kind_of_; using _base_type_ = kind_of_;
static constexpr auto _quantity_spec_ = Q{}; static constexpr auto _quantity_spec_ = Q{};
@@ -507,10 +507,10 @@ struct kind_of_<Q> final : Q::_base_type_ {
#if MP_UNITS_COMP_CLANG #if MP_UNITS_COMP_CLANG
template<typename Q> template<typename Q>
requires detail::QuantitySpecWithNoSpecifiers<Q> && (detail::get_kind_tree_root(Q{}) == Q{}) requires detail::QuantitySpecWithNoSpecifiers<Q> && (decltype(detail::get_kind_tree_root(Q{})){} == Q{})
#else #else
template<detail::QuantitySpecWithNoSpecifiers Q> template<detail::QuantitySpecWithNoSpecifiers Q>
requires(detail::get_kind_tree_root(Q{}) == Q{}) requires(decltype(detail::get_kind_tree_root(Q{})){} == Q{})
#endif #endif
struct kind_of_<Q> final : quantity_spec<kind_of_<Q>, Q{}>::_base_type_ { struct kind_of_<Q> final : quantity_spec<kind_of_<Q>, Q{}>::_base_type_ {
using _base_type_ = kind_of_; using _base_type_ = kind_of_;
@@ -519,7 +519,7 @@ struct kind_of_<Q> final : quantity_spec<kind_of_<Q>, Q{}>::_base_type_ {
#endif #endif
MP_UNITS_EXPORT template<detail::QuantitySpecWithNoSpecifiers auto Q> MP_UNITS_EXPORT template<detail::QuantitySpecWithNoSpecifiers auto Q>
requires(detail::get_kind_tree_root(Q) == Q) requires(decltype(detail::get_kind_tree_root(Q)){} == Q)
inline constexpr kind_of_<MP_UNITS_REMOVE_CONST(decltype(Q))> kind_of; inline constexpr kind_of_<MP_UNITS_REMOVE_CONST(decltype(Q))> kind_of;
namespace detail { namespace detail {
@@ -820,71 +820,59 @@ template<int Complexity, NamedQuantitySpec Q>
template<typename NumFrom, typename... NumsFrom, typename DenFrom, typename... DensFrom, typename NumTo, template<typename NumFrom, typename... NumsFrom, typename DenFrom, typename... DensFrom, typename NumTo,
typename... NumsTo, typename DenTo, typename... DensTo> typename... NumsTo, typename DenTo, typename... DensTo>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<NumFrom, NumsFrom...> num_from, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<NumFrom, NumsFrom...> num_from,
type_list<DenFrom, DensFrom...> den_from, type_list<DenFrom, DensFrom...> den_from,
type_list<NumTo, NumsTo...> num_to, type_list<NumTo, NumsTo...> num_to,
type_list<DenTo, DensTo...> den_to); type_list<DenTo, DensTo...> den_to);
template<typename DenFrom, typename... DensFrom, typename NumTo, typename... NumsTo, typename DenTo, typename... DensTo> template<typename DenFrom, typename... DensFrom, typename NumTo, typename... NumsTo, typename DenTo, typename... DensTo>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<>, type_list<DenFrom, DensFrom...>,
type_list<DenFrom, DensFrom...>, type_list<NumTo, NumsTo...>, type_list<DenTo, DensTo...>);
type_list<NumTo, NumsTo...>,
type_list<DenTo, DensTo...>);
template<typename NumFrom, typename... NumsFrom, typename NumTo, typename... NumsTo, typename DenTo, typename... DensTo> template<typename NumFrom, typename... NumsFrom, typename NumTo, typename... NumsTo, typename DenTo, typename... DensTo>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<NumFrom, NumsFrom...>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<NumFrom, NumsFrom...>, type_list<>,
type_list<>, type_list<NumTo, NumsTo...>, type_list<NumTo, NumsTo...>, type_list<DenTo, DensTo...>);
type_list<DenTo, DensTo...>);
template<typename NumFrom, typename... NumsFrom, typename DenFrom, typename... DensFrom, typename DenTo, template<typename NumFrom, typename... NumsFrom, typename DenFrom, typename... DensFrom, typename DenTo,
typename... DensTo> typename... DensTo>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<NumFrom, NumsFrom...>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<NumFrom, NumsFrom...>,
type_list<DenFrom, DensFrom...>, type_list<DenFrom, DensFrom...>, type_list<>,
type_list<>, type_list<DenTo, DensTo...>); type_list<DenTo, DensTo...>);
template<typename NumFrom, typename... NumsFrom, typename DenFrom, typename... DensFrom, typename NumTo, template<typename NumFrom, typename... NumsFrom, typename DenFrom, typename... DensFrom, typename NumTo,
typename... NumsTo> typename... NumsTo>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<NumFrom, NumsFrom...>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<NumFrom, NumsFrom...>,
type_list<DenFrom, DensFrom...>, type_list<DenFrom, DensFrom...>, type_list<NumTo, NumsTo...>,
type_list<NumTo, NumsTo...>, type_list<>); type_list<>);
template<typename NumFrom, typename... NumsFrom, typename NumTo, typename... NumsTo> template<typename NumFrom, typename... NumsFrom, typename NumTo, typename... NumsTo>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<NumFrom, NumsFrom...>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<NumFrom, NumsFrom...>, type_list<>,
type_list<>, type_list<NumTo, NumsTo...>, type_list<NumTo, NumsTo...>, type_list<>);
type_list<>);
template<typename DenFrom, typename... DensFrom, typename DenTo, typename... DensTo> template<typename DenFrom, typename... DensFrom, typename DenTo, typename... DensTo>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<>, type_list<DenFrom, DensFrom...>, type_list<>,
type_list<DenFrom, DensFrom...>, type_list<DenTo, DensTo...>);
type_list<>, type_list<DenTo, DensTo...>);
template<typename... NumsFrom, typename... DensFrom> template<typename... NumsFrom, typename... DensFrom>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<NumsFrom...>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<NumsFrom...>, type_list<DensFrom...>, type_list<>,
type_list<DensFrom...>, type_list<>, type_list<>);
type_list<>);
template<typename... NumsTo, typename... DensTo> template<typename... NumsTo, typename... DensTo>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<>, type_list<>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<>, type_list<>, type_list<NumsTo...>,
type_list<NumsTo...>, type_list<DensTo...>);
type_list<DensTo...>);
template<typename... NumsFrom> template<typename... NumsFrom>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<NumsFrom...>, type_list<>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<NumsFrom...>, type_list<>, type_list<>, type_list<>);
type_list<>, type_list<>);
template<typename... DensFrom> template<typename... DensFrom>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<>, type_list<DensFrom...>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<>, type_list<DensFrom...>, type_list<>, type_list<>);
type_list<>, type_list<>);
template<typename... NumsTo> template<typename... NumsTo>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<>, type_list<>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<>, type_list<>, type_list<NumsTo...>, type_list<>);
type_list<NumsTo...>, type_list<>);
template<typename... DensFrom> template<typename... DensFrom>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<>, type_list<>, type_list<>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<>, type_list<>, type_list<>, type_list<DensFrom...>);
type_list<DensFrom...>);
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<>, type_list<>, type_list<>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<>, type_list<>, type_list<>, type_list<>);
type_list<>);
enum class prepend_rest : std::int8_t { no, first, second }; enum class prepend_rest : std::int8_t { no, first, second };
@@ -946,49 +934,63 @@ template<typename From, typename To>
enum class process_entities : std::int8_t { numerators, denominators, from, to }; enum class process_entities : std::int8_t { numerators, denominators, from, to };
template<process_entities Entities, auto Ext, TypeList NumFrom, TypeList DenFrom, TypeList NumTo, TypeList DenTo> template<process_entities Entities, auto Ext, TypeList NumFrom, TypeList DenFrom, TypeList NumTo, TypeList DenTo>
[[nodiscard]] consteval specs_convertible_result process_num_den(NumFrom num_from, DenFrom den_from, NumTo num_to, [[nodiscard]] consteval auto process_num_den(NumFrom num_from, DenFrom den_from, NumTo num_to, DenTo den_to)
DenTo den_to)
{ {
constexpr auto res = convertible_impl(Ext.from, Ext.to); constexpr auto res = decltype(convertible_impl(Ext.from, Ext.to))::value;
if constexpr (Ext.prepend == prepend_rest::no) if constexpr (Ext.prepend == prepend_rest::no)
return min(res, are_ingredients_convertible(num_from, den_from, num_to, den_to)); return std::integral_constant<specs_convertible_result, min(res, decltype(are_ingredients_convertible(
num_from, den_from, num_to, den_to))::value)>{};
else { else {
using elem = decltype(Ext.elem); using elem = decltype(Ext.elem);
if constexpr (Entities == process_entities::numerators) { if constexpr (Entities == process_entities::numerators) {
if constexpr (Ext.prepend == prepend_rest::first) if constexpr (Ext.prepend == prepend_rest::first)
return min(res, are_ingredients_convertible(type_list_push_front<NumFrom, elem>{}, den_from, num_to, den_to)); return std::integral_constant<specs_convertible_result, min(res, decltype(are_ingredients_convertible(
type_list_push_front<NumFrom, elem>{},
den_from, num_to, den_to))::value)>{};
else else
return min(res, are_ingredients_convertible(num_from, den_from, type_list_push_front<NumTo, elem>{}, den_to)); return std::integral_constant<specs_convertible_result,
min(res, decltype(are_ingredients_convertible(num_from, den_from,
type_list_push_front<NumTo, elem>{},
den_to))::value)>{};
} else { } else {
if constexpr (Ext.prepend == prepend_rest::first) if constexpr (Ext.prepend == prepend_rest::first)
return min(res, are_ingredients_convertible(num_from, type_list_push_front<DenFrom, elem>{}, num_to, den_to)); return std::integral_constant<specs_convertible_result,
min(res, decltype(are_ingredients_convertible(
num_from, type_list_push_front<DenFrom, elem>{}, num_to,
den_to))::value)>{};
else else
return min(res, are_ingredients_convertible(num_from, den_from, num_to, type_list_push_front<DenTo, elem>{})); return std::integral_constant<specs_convertible_result,
min(res, decltype(are_ingredients_convertible(
num_from, den_from, num_to,
type_list_push_front<DenTo, elem>{}))::value)>{};
} }
} }
} }
template<process_entities Entities, auto Ext, TypeList NumFrom, TypeList DenFrom, TypeList NumTo, TypeList DenTo> template<process_entities Entities, auto Ext, TypeList NumFrom, TypeList DenFrom, TypeList NumTo, TypeList DenTo>
[[nodiscard]] consteval specs_convertible_result process_extracted(NumFrom num_from, DenFrom den_from, NumTo num_to, [[nodiscard]] consteval auto process_extracted(NumFrom num_from, DenFrom den_from, NumTo num_to, DenTo den_to)
DenTo den_to)
{ {
if constexpr (Entities == process_entities::numerators || Entities == process_entities::denominators) { if constexpr (Entities == process_entities::numerators || Entities == process_entities::denominators) {
return process_num_den<Entities, Ext>(num_from, den_from, num_to, den_to); return decltype(process_num_den<Entities, Ext>(num_from, den_from, num_to, den_to)){};
} else { } else {
if constexpr (Ext.prepend == prepend_rest::no) if constexpr (Ext.prepend == prepend_rest::no)
return are_ingredients_convertible(num_from, den_from, num_to, den_to); return decltype(are_ingredients_convertible(num_from, den_from, num_to, den_to)){};
else { else {
using elem = decltype(Ext.elem); using elem = decltype(Ext.elem);
if constexpr (Entities == process_entities::from) { if constexpr (Entities == process_entities::from) {
if constexpr (Ext.prepend == prepend_rest::first) if constexpr (Ext.prepend == prepend_rest::first)
return are_ingredients_convertible(type_list_push_front<NumFrom, elem>{}, den_from, num_to, den_to); return decltype(are_ingredients_convertible(type_list_push_front<NumFrom, elem>{}, den_from, num_to,
den_to)){};
else else
return are_ingredients_convertible(num_from, type_list_push_front<DenFrom, elem>{}, num_to, den_to); return decltype(are_ingredients_convertible(num_from, type_list_push_front<DenFrom, elem>{}, num_to,
den_to)){};
} else { } else {
if constexpr (Ext.prepend == prepend_rest::first) if constexpr (Ext.prepend == prepend_rest::first)
return are_ingredients_convertible(num_from, den_from, type_list_push_front<NumTo, elem>{}, den_to); return decltype(are_ingredients_convertible(num_from, den_from, type_list_push_front<NumTo, elem>{},
den_to)){};
else else
return are_ingredients_convertible(num_from, den_from, num_to, type_list_push_front<DenTo, elem>{}); return decltype(are_ingredients_convertible(num_from, den_from, num_to,
type_list_push_front<DenTo, elem>{})){};
} }
} }
} }
@@ -996,23 +998,23 @@ template<process_entities Entities, auto Ext, TypeList NumFrom, TypeList DenFrom
template<typename NumFrom, typename... NumsFrom, typename DenFrom, typename... DensFrom, typename NumTo, template<typename NumFrom, typename... NumsFrom, typename DenFrom, typename... DensFrom, typename NumTo,
typename... NumsTo, typename DenTo, typename... DensTo> typename... NumsTo, typename DenTo, typename... DensTo>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<NumFrom, NumsFrom...> num_from, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<NumFrom, NumsFrom...> num_from,
type_list<DenFrom, DensFrom...> den_from, type_list<DenFrom, DensFrom...> den_from,
type_list<NumTo, NumsTo...> num_to, type_list<NumTo, NumsTo...> num_to,
type_list<DenTo, DensTo...> den_to) type_list<DenTo, DensTo...> den_to)
{ {
if constexpr (constexpr auto extN = extract_convertible_quantities(NumFrom{}, NumTo{}); extN.same_dimension) if constexpr (constexpr auto extN = extract_convertible_quantities(NumFrom{}, NumTo{}); extN.same_dimension)
return process_extracted<process_entities::numerators, extN>(type_list<NumsFrom...>{}, den_from, return decltype(process_extracted<process_entities::numerators, extN>(type_list<NumsFrom...>{}, den_from,
type_list<NumsTo...>{}, den_to); type_list<NumsTo...>{}, den_to)){};
else if constexpr (constexpr auto extD = extract_convertible_quantities(DenFrom{}, DenTo{}); extD.same_dimension) else if constexpr (constexpr auto extD = extract_convertible_quantities(DenFrom{}, DenTo{}); extD.same_dimension)
return process_extracted<process_entities::denominators, extD>(num_from, type_list<DensFrom...>{}, num_to, return decltype(process_extracted<process_entities::denominators, extD>(num_from, type_list<DensFrom...>{}, num_to,
type_list<DensTo...>{}); type_list<DensTo...>{})){};
else if constexpr (constexpr auto extF = extract_convertible_quantities(NumFrom{}, DenFrom{}); extF.same_dimension) else if constexpr (constexpr auto extF = extract_convertible_quantities(NumFrom{}, DenFrom{}); extF.same_dimension)
return process_extracted<process_entities::from, extF>(type_list<NumsFrom...>{}, type_list<DensFrom...>{}, num_to, return decltype(process_extracted<process_entities::from, extF>(type_list<NumsFrom...>{}, type_list<DensFrom...>{},
den_to); num_to, den_to)){};
else if constexpr (constexpr auto extT = extract_convertible_quantities(NumTo{}, DenTo{}); extT.same_dimension) else if constexpr (constexpr auto extT = extract_convertible_quantities(NumTo{}, DenTo{}); extT.same_dimension)
return process_extracted<process_entities::to, extT>(num_from, den_from, type_list<NumsTo...>{}, return decltype(process_extracted<process_entities::to, extT>(num_from, den_from, type_list<NumsTo...>{},
type_list<DensTo...>{}); type_list<DensTo...>{})){};
else { else {
constexpr auto num_from_compl = decltype(get_complexity(NumFrom{}))::value; constexpr auto num_from_compl = decltype(get_complexity(NumFrom{}))::value;
constexpr auto den_from_compl = decltype(get_complexity(DenFrom{}))::value; constexpr auto den_from_compl = decltype(get_complexity(DenFrom{}))::value;
@@ -1021,45 +1023,52 @@ template<typename NumFrom, typename... NumsFrom, typename DenFrom, typename... D
constexpr auto max_compl = max({num_from_compl, num_to_compl, den_from_compl, den_to_compl}); constexpr auto max_compl = max({num_from_compl, num_to_compl, den_from_compl, den_to_compl});
if constexpr (max_compl > 1) { if constexpr (max_compl > 1) {
if constexpr (num_from_compl == max_compl) { if constexpr (num_from_compl == max_compl) {
constexpr auto res = explode_to_equation(NumFrom{}); constexpr auto res = decltype(explode_to_equation(NumFrom{})){};
return convertible_impl( return decltype(convertible_impl(decltype(decltype((res.equation * ... * map_power(NumsFrom{}))){} /
(res.equation * ... * map_power(NumsFrom{})) / (map_power(DenFrom{}) * ... * map_power(DensFrom{})), decltype((map_power(DenFrom{}) * ... * map_power(DensFrom{}))){}){},
(map_power(NumTo{}) * ... * map_power(NumsTo{})) / (map_power(DenTo{}) * ... * map_power(DensTo{}))); decltype(decltype((map_power(NumTo{}) * ... * map_power(NumsTo{}))){} /
decltype((map_power(DenTo{}) * ... * map_power(DensTo{}))){}){})){};
} else if constexpr (den_from_compl == max_compl) { } else if constexpr (den_from_compl == max_compl) {
constexpr auto res = explode_to_equation(DenFrom{}); constexpr auto res = decltype(explode_to_equation(DenFrom{})){};
return convertible_impl( return decltype(convertible_impl(decltype(decltype((map_power(NumFrom{}) * ... * map_power(NumsFrom{}))){} /
(map_power(NumFrom{}) * ... * map_power(NumsFrom{})) / (res.equation * ... * map_power(DensFrom{})), decltype((res.equation * ... * map_power(DensFrom{}))){}){},
(map_power(NumTo{}) * ... * map_power(NumsTo{})) / (map_power(DenTo{}) * ... * map_power(DensTo{}))); decltype(decltype((map_power(NumTo{}) * ... * map_power(NumsTo{}))){} /
decltype((map_power(DenTo{}) * ... * map_power(DensTo{}))){}){})){};
} else if constexpr (num_to_compl == max_compl) { } else if constexpr (num_to_compl == max_compl) {
constexpr auto res = explode_to_equation(NumTo{}); constexpr auto res = decltype(explode_to_equation(NumTo{})){};
return min(res.result, convertible_impl((map_power(NumFrom{}) * ... * map_power(NumsFrom{})) / return std::integral_constant<
(map_power(DenFrom{}) * ... * map_power(DensFrom{})), specs_convertible_result,
(res.equation * ... * map_power(NumsTo{})) / min(res.result, decltype(convertible_impl(
(map_power(DenTo{}) * ... * map_power(DensTo{})))); decltype(decltype((map_power(NumFrom{}) * ... * map_power(NumsFrom{}))){} /
decltype((map_power(DenFrom{}) * ... * map_power(DensFrom{}))){}){},
decltype(decltype((res.equation * ... * map_power(NumsTo{}))){} /
decltype((map_power(DenTo{}) * ... * map_power(DensTo{}))){}){}))::value)>{};
} else { } else {
constexpr auto res = explode_to_equation(DenTo{}); constexpr auto res = decltype(explode_to_equation(DenTo{})){};
return min(res.result, convertible_impl((map_power(NumFrom{}) * ... * map_power(NumsFrom{})) / return std::integral_constant<
(map_power(DenFrom{}) * ... * map_power(DensFrom{})), specs_convertible_result,
(map_power(NumTo{}) * ... * map_power(NumsTo{})) / min(res.result,
(res.equation * ... * map_power(DensTo{})))); decltype(convertible_impl(decltype(decltype((map_power(NumFrom{}) * ... * map_power(NumsFrom{}))){} /
decltype((map_power(DenFrom{}) * ... * map_power(DensFrom{}))){}){},
decltype(decltype((map_power(NumTo{}) * ... * map_power(NumsTo{}))){} /
decltype((res.equation * ... * map_power(DensTo{}))){}){}))::value)>{};
} }
} } else
return std::integral_constant<specs_convertible_result, specs_convertible_result::no>{};
} }
return specs_convertible_result::no;
} }
template<typename DenFrom, typename... DensFrom, typename NumTo, typename... NumsTo, typename DenTo, typename... DensTo> template<typename DenFrom, typename... DensFrom, typename NumTo, typename... NumsTo, typename DenTo, typename... DensTo>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<> num_from, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<> num_from, type_list<DenFrom, DensFrom...> den_from,
type_list<DenFrom, DensFrom...> den_from, type_list<NumTo, NumsTo...> num_to,
type_list<NumTo, NumsTo...> num_to, type_list<DenTo, DensTo...>)
type_list<DenTo, DensTo...>)
{ {
if constexpr (constexpr auto extD = extract_convertible_quantities(DenFrom{}, DenTo{}); extD.same_dimension) if constexpr (constexpr auto extD = extract_convertible_quantities(DenFrom{}, DenTo{}); extD.same_dimension)
return process_extracted<process_entities::denominators, extD>(num_from, type_list<DensFrom...>{}, num_to, return decltype(process_extracted<process_entities::denominators, extD>(num_from, type_list<DensFrom...>{}, num_to,
type_list<DensTo...>{}); type_list<DensTo...>{})){};
else if constexpr (constexpr auto extT = extract_convertible_quantities(NumTo{}, DenTo{}); extT.same_dimension) else if constexpr (constexpr auto extT = extract_convertible_quantities(NumTo{}, DenTo{}); extT.same_dimension)
return process_extracted<process_entities::to, extT>(num_from, den_from, type_list<NumsTo...>{}, return decltype(process_extracted<process_entities::to, extT>(num_from, den_from, type_list<NumsTo...>{},
type_list<DensTo...>{}); type_list<DensTo...>{})){};
else { else {
constexpr auto den_from_compl = decltype(get_complexity(DenFrom{}))::value; constexpr auto den_from_compl = decltype(get_complexity(DenFrom{}))::value;
constexpr auto num_to_compl = decltype(get_complexity(NumTo{}))::value; constexpr auto num_to_compl = decltype(get_complexity(NumTo{}))::value;
@@ -1067,38 +1076,46 @@ template<typename DenFrom, typename... DensFrom, typename NumTo, typename... Num
constexpr auto max_compl = max({num_to_compl, den_from_compl, den_to_compl}); constexpr auto max_compl = max({num_to_compl, den_from_compl, den_to_compl});
if constexpr (max_compl > 1) { if constexpr (max_compl > 1) {
if constexpr (den_from_compl == max_compl) { if constexpr (den_from_compl == max_compl) {
constexpr auto res = explode_to_equation(DenFrom{}); constexpr auto res = decltype(explode_to_equation(DenFrom{})){};
return convertible_impl( return decltype(convertible_impl(
dimensionless / (res.equation * ... * map_power(DensFrom{})), decltype(dimensionless / decltype((res.equation * ... * map_power(DensFrom{}))){}){},
(map_power(NumTo{}) * ... * map_power(NumsTo{})) / (map_power(DenTo{}) * ... * map_power(DensTo{}))); decltype(decltype((map_power(NumTo{}) * ... * map_power(NumsTo{}))){} /
decltype((map_power(DenTo{}) * ... * map_power(DensTo{}))){}){})){};
} else if constexpr (num_to_compl == max_compl) { } else if constexpr (num_to_compl == max_compl) {
constexpr auto res = explode_to_equation(NumTo{}); constexpr auto res = decltype(explode_to_equation(NumTo{})){};
return min(res.result, convertible_impl(dimensionless / (map_power(DenFrom{}) * ... * map_power(DensFrom{})), return std::integral_constant<
(res.equation * ... * map_power(NumsTo{})) / specs_convertible_result,
(map_power(DenTo{}) * ... * map_power(DensTo{})))); min(res.result,
decltype(convertible_impl(
decltype(dimensionless / decltype((map_power(DenFrom{}) * ... * map_power(DensFrom{}))){}){},
decltype(decltype((res.equation * ... * map_power(NumsTo{}))){} /
decltype((map_power(DenTo{}) * ... * map_power(DensTo{}))){}){}))::value)>{};
} else { } else {
constexpr auto res = explode_to_equation(DenTo{}); constexpr auto res = decltype(explode_to_equation(DenTo{})){};
return min(res.result, convertible_impl(dimensionless / (map_power(DenFrom{}) * ... * map_power(DensFrom{})), return std::integral_constant<
(map_power(NumTo{}) * ... * map_power(NumsTo{})) / specs_convertible_result,
(res.equation * ... * map_power(DensTo{})))); min(res.result,
decltype(convertible_impl(
decltype(dimensionless / decltype((map_power(DenFrom{}) * ... * map_power(DensFrom{}))){}){},
decltype(decltype((map_power(NumTo{}) * ... * map_power(NumsTo{}))){} /
decltype((res.equation * ... * map_power(DensTo{}))){}){}))::value)>{};
} }
} } else
return std::integral_constant<specs_convertible_result, specs_convertible_result::no>{};
} }
return specs_convertible_result::no;
} }
template<typename NumFrom, typename... NumsFrom, typename NumTo, typename... NumsTo, typename DenTo, typename... DensTo> template<typename NumFrom, typename... NumsFrom, typename NumTo, typename... NumsTo, typename DenTo, typename... DensTo>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<NumFrom, NumsFrom...> num_from, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<NumFrom, NumsFrom...> num_from, type_list<> den_from,
type_list<> den_from, type_list<NumTo, NumsTo...>,
type_list<NumTo, NumsTo...>, type_list<DenTo, DensTo...> den_to)
type_list<DenTo, DensTo...> den_to)
{ {
if constexpr (constexpr auto extN = extract_convertible_quantities(NumFrom{}, NumTo{}); extN.same_dimension) if constexpr (constexpr auto extN = extract_convertible_quantities(NumFrom{}, NumTo{}); extN.same_dimension)
return process_extracted<process_entities::numerators, extN>(type_list<NumsFrom...>{}, den_from, return decltype(process_extracted<process_entities::numerators, extN>(type_list<NumsFrom...>{}, den_from,
type_list<NumsTo...>{}, den_to); type_list<NumsTo...>{}, den_to)){};
else if constexpr (constexpr auto extT = extract_convertible_quantities(NumTo{}, DenTo{}); extT.same_dimension) else if constexpr (constexpr auto extT = extract_convertible_quantities(NumTo{}, DenTo{}); extT.same_dimension)
return process_extracted<process_entities::to, extT>(num_from, den_from, type_list<NumsTo...>{}, return decltype(process_extracted<process_entities::to, extT>(num_from, den_from, type_list<NumsTo...>{},
type_list<DensTo...>{}); type_list<DensTo...>{})){};
else { else {
constexpr auto num_from_compl = decltype(get_complexity(NumFrom{}))::value; constexpr auto num_from_compl = decltype(get_complexity(NumFrom{}))::value;
constexpr auto num_to_compl = decltype(get_complexity(NumTo{}))::value; constexpr auto num_to_compl = decltype(get_complexity(NumTo{}))::value;
@@ -1106,39 +1123,44 @@ template<typename NumFrom, typename... NumsFrom, typename NumTo, typename... Num
constexpr auto max_compl = max({num_from_compl, num_to_compl, den_to_compl}); constexpr auto max_compl = max({num_from_compl, num_to_compl, den_to_compl});
if constexpr (max_compl > 1) { if constexpr (max_compl > 1) {
if constexpr (num_from_compl == max_compl) { if constexpr (num_from_compl == max_compl) {
constexpr auto res = explode_to_equation(NumFrom{}); constexpr auto res = decltype(explode_to_equation(NumFrom{})){};
return convertible_impl( return decltype(convertible_impl(decltype((res.equation * ... * map_power(NumsFrom{}))){},
(res.equation * ... * map_power(NumsFrom{})), decltype(decltype((map_power(NumTo{}) * ... * map_power(NumsTo{}))){} /
(map_power(NumTo{}) * ... * map_power(NumsTo{})) / (map_power(DenTo{}) * ... * map_power(DensTo{}))); decltype((map_power(DenTo{}) * ... * map_power(DensTo{}))){}){})){};
} else if constexpr (num_to_compl == max_compl) { } else if constexpr (num_to_compl == max_compl) {
constexpr auto res = explode_to_equation(NumTo{}); constexpr auto res = decltype(explode_to_equation(NumTo{})){};
return min(res.result, convertible_impl((map_power(NumFrom{}) * ... * map_power(NumsFrom{})), return std::integral_constant<
(res.equation * ... * map_power(NumsTo{})) / specs_convertible_result,
(map_power(DenTo{}) * ... * map_power(DensTo{})))); min(res.result, decltype(convertible_impl(
decltype((map_power(NumFrom{}) * ... * map_power(NumsFrom{}))){},
decltype(decltype((res.equation * ... * map_power(NumsTo{}))){} /
decltype((map_power(DenTo{}) * ... * map_power(DensTo{}))){}){}))::value)>{};
} else { } else {
constexpr auto res = explode_to_equation(DenTo{}); constexpr auto res = decltype(explode_to_equation(DenTo{})){};
return min(res.result, convertible_impl((map_power(NumFrom{}) * ... * map_power(NumsFrom{})), return std::integral_constant<
(map_power(NumTo{}) * ... * map_power(NumsTo{})) / specs_convertible_result,
(res.equation * ... * map_power(DensTo{})))); min(res.result,
decltype(convertible_impl(decltype((map_power(NumFrom{}) * ... * map_power(NumsFrom{}))){},
decltype(decltype((map_power(NumTo{}) * ... * map_power(NumsTo{}))){} /
decltype((res.equation * ... * map_power(DensTo{}))){}){}))::value)>{};
} }
} } else
return std::integral_constant<specs_convertible_result, specs_convertible_result::no>{};
} }
return specs_convertible_result::no;
} }
template<typename NumFrom, typename... NumsFrom, typename DenFrom, typename... DensFrom, typename DenTo, template<typename NumFrom, typename... NumsFrom, typename DenFrom, typename... DensFrom, typename DenTo,
typename... DensTo> typename... DensTo>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<NumFrom, NumsFrom...> num_from, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<NumFrom, NumsFrom...> num_from,
type_list<DenFrom, DensFrom...>, type_list<DenFrom, DensFrom...>, type_list<> num_to,
type_list<> num_to, type_list<DenTo, DensTo...> den_to)
type_list<DenTo, DensTo...> den_to)
{ {
if constexpr (constexpr auto extD = extract_convertible_quantities(DenFrom{}, DenTo{}); extD.same_dimension) if constexpr (constexpr auto extD = extract_convertible_quantities(DenFrom{}, DenTo{}); extD.same_dimension)
return process_extracted<process_entities::denominators, extD>(num_from, type_list<DensFrom...>{}, num_to, return decltype(process_extracted<process_entities::denominators, extD>(num_from, type_list<DensFrom...>{}, num_to,
type_list<DensTo...>{}); type_list<DensTo...>{})){};
else if constexpr (constexpr auto extF = extract_convertible_quantities(NumFrom{}, DenFrom{}); extF.same_dimension) else if constexpr (constexpr auto extF = extract_convertible_quantities(NumFrom{}, DenFrom{}); extF.same_dimension)
return process_extracted<process_entities::from, extF>(type_list<NumsFrom...>{}, type_list<DensFrom...>{}, num_to, return decltype(process_extracted<process_entities::from, extF>(type_list<NumsFrom...>{}, type_list<DensFrom...>{},
den_to); num_to, den_to)){};
else { else {
constexpr auto num_from_compl = decltype(get_complexity(NumFrom{}))::value; constexpr auto num_from_compl = decltype(get_complexity(NumFrom{}))::value;
constexpr auto den_from_compl = decltype(get_complexity(DenFrom{}))::value; constexpr auto den_from_compl = decltype(get_complexity(DenFrom{}))::value;
@@ -1146,39 +1168,44 @@ template<typename NumFrom, typename... NumsFrom, typename DenFrom, typename... D
constexpr auto max_compl = max({num_from_compl, den_from_compl, den_to_compl}); constexpr auto max_compl = max({num_from_compl, den_from_compl, den_to_compl});
if constexpr (max_compl > 1) { if constexpr (max_compl > 1) {
if constexpr (num_from_compl == max_compl) { if constexpr (num_from_compl == max_compl) {
constexpr auto res = explode_to_equation(NumFrom{}); constexpr auto res = decltype(explode_to_equation(NumFrom{})){};
return convertible_impl( return decltype(convertible_impl(
(res.equation * ... * map_power(NumsFrom{})) / (map_power(DenFrom{}) * ... * map_power(DensFrom{})), decltype(decltype((res.equation * ... * map_power(NumsFrom{}))){} /
dimensionless / (map_power(DenTo{}) * ... * map_power(DensTo{}))); decltype((map_power(DenFrom{}) * ... * map_power(DensFrom{}))){}){},
decltype(dimensionless / decltype((map_power(DenTo{}) * ... * map_power(DensTo{}))){}){})){};
} else if constexpr (den_from_compl == max_compl) { } else if constexpr (den_from_compl == max_compl) {
constexpr auto res = explode_to_equation(DenFrom{}); constexpr auto res = decltype(explode_to_equation(DenFrom{})){};
return convertible_impl( return decltype(convertible_impl(
(map_power(NumFrom{}) * ... * map_power(NumsFrom{})) / (res.equation * ... * map_power(DensFrom{})), decltype(decltype((map_power(NumFrom{}) * ... * map_power(NumsFrom{}))){} /
dimensionless / (map_power(DenTo{}) * ... * map_power(DensTo{}))); decltype((res.equation * ... * map_power(DensFrom{}))){}){},
decltype(dimensionless / decltype((map_power(DenTo{}) * ... * map_power(DensTo{}))){}){})){};
} else { } else {
constexpr auto res = explode_to_equation(DenTo{}); constexpr auto res = decltype(explode_to_equation(DenTo{})){};
return min(res.result, convertible_impl((map_power(NumFrom{}) * ... * map_power(NumsFrom{})) / return std::integral_constant<
(map_power(DenFrom{}) * ... * map_power(DensFrom{})), specs_convertible_result,
dimensionless / (res.equation * ... * map_power(DensTo{})))); min(res.result,
decltype(convertible_impl(
decltype(decltype((map_power(NumFrom{}) * ... * map_power(NumsFrom{}))){} /
decltype((map_power(DenFrom{}) * ... * map_power(DensFrom{}))){}){},
decltype(dimensionless / decltype((res.equation * ... * map_power(DensTo{}))){}){}))::value)>{};
} }
} } else
return std::integral_constant<specs_convertible_result, specs_convertible_result::no>{};
} }
return specs_convertible_result::no;
} }
template<typename NumFrom, typename... NumsFrom, typename DenFrom, typename... DensFrom, typename NumTo, template<typename NumFrom, typename... NumsFrom, typename DenFrom, typename... DensFrom, typename NumTo,
typename... NumsTo> typename... NumsTo>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<NumFrom, NumsFrom...>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<NumFrom, NumsFrom...>,
type_list<DenFrom, DensFrom...> den_from, type_list<DenFrom, DensFrom...> den_from,
type_list<NumTo, NumsTo...> num_to, type_list<NumTo, NumsTo...> num_to, type_list<> den_to)
type_list<> den_to)
{ {
if constexpr (constexpr auto extN = extract_convertible_quantities(NumFrom{}, NumTo{}); extN.same_dimension) if constexpr (constexpr auto extN = extract_convertible_quantities(NumFrom{}, NumTo{}); extN.same_dimension)
return process_extracted<process_entities::numerators, extN>(type_list<NumsFrom...>{}, den_from, return decltype(process_extracted<process_entities::numerators, extN>(type_list<NumsFrom...>{}, den_from,
type_list<NumsTo...>{}, den_to); type_list<NumsTo...>{}, den_to)){};
else if constexpr (constexpr auto extF = extract_convertible_quantities(NumFrom{}, DenFrom{}); extF.same_dimension) else if constexpr (constexpr auto extF = extract_convertible_quantities(NumFrom{}, DenFrom{}); extF.same_dimension)
return process_extracted<process_entities::from, extF>(type_list<NumsFrom...>{}, type_list<DensFrom...>{}, num_to, return decltype(process_extracted<process_entities::from, extF>(type_list<NumsFrom...>{}, type_list<DensFrom...>{},
den_to); num_to, den_to)){};
else { else {
constexpr auto num_from_compl = decltype(get_complexity(NumFrom{}))::value; constexpr auto num_from_compl = decltype(get_complexity(NumFrom{}))::value;
constexpr auto den_from_compl = decltype(get_complexity(DenFrom{}))::value; constexpr auto den_from_compl = decltype(get_complexity(DenFrom{}))::value;
@@ -1186,176 +1213,178 @@ template<typename NumFrom, typename... NumsFrom, typename DenFrom, typename... D
constexpr auto max_compl = max({num_from_compl, num_to_compl, den_from_compl}); constexpr auto max_compl = max({num_from_compl, num_to_compl, den_from_compl});
if constexpr (max_compl > 1) { if constexpr (max_compl > 1) {
if constexpr (num_from_compl == max_compl) { if constexpr (num_from_compl == max_compl) {
constexpr auto res = explode_to_equation(NumFrom{}); constexpr auto res = decltype(explode_to_equation(NumFrom{})){};
return convertible_impl( return decltype(convertible_impl(decltype(decltype((res.equation * ... * map_power(NumsFrom{}))){} /
(res.equation * ... * map_power(NumsFrom{})) / (map_power(DenFrom{}) * ... * map_power(DensFrom{})), decltype((map_power(DenFrom{}) * ... * map_power(DensFrom{}))){}){},
(map_power(NumTo{}) * ... * map_power(NumsTo{}))); decltype((map_power(NumTo{}) * ... * map_power(NumsTo{}))){})){};
} else if constexpr (den_from_compl == max_compl) { } else if constexpr (den_from_compl == max_compl) {
constexpr auto res = explode_to_equation(DenFrom{}); constexpr auto res = decltype(explode_to_equation(DenFrom{})){};
return convertible_impl( return decltype(convertible_impl(decltype(decltype((map_power(NumFrom{}) * ... * map_power(NumsFrom{}))){} /
(map_power(NumFrom{}) * ... * map_power(NumsFrom{})) / (res.equation * ... * map_power(DensFrom{})), decltype((res.equation * ... * map_power(DensFrom{}))){}){},
(map_power(NumTo{}) * ... * map_power(NumsTo{}))); decltype((map_power(NumTo{}) * ... * map_power(NumsTo{}))){})){};
} else { } else {
constexpr auto res = explode_to_equation(NumTo{}); constexpr auto res = decltype(explode_to_equation(NumTo{})){};
return min(res.result, convertible_impl((map_power(NumFrom{}) * ... * map_power(NumsFrom{})) / return std::integral_constant<
(map_power(DenFrom{}) * ... * map_power(DensFrom{})), specs_convertible_result,
(res.equation * ... * map_power(NumsTo{})))); min(res.result,
decltype(convertible_impl(decltype(decltype((map_power(NumFrom{}) * ... * map_power(NumsFrom{}))){} /
decltype((map_power(DenFrom{}) * ... * map_power(DensFrom{}))){}){},
decltype((res.equation * ... * map_power(NumsTo{}))){}))::value)>{};
} }
} } else
return std::integral_constant<specs_convertible_result, specs_convertible_result::no>{};
} }
return specs_convertible_result::no;
} }
template<typename NumFrom, typename... NumsFrom, typename NumTo, typename... NumsTo> template<typename NumFrom, typename... NumsFrom, typename NumTo, typename... NumsTo>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<NumFrom, NumsFrom...>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<NumFrom, NumsFrom...>, type_list<> den_from,
type_list<> den_from, type_list<NumTo, NumsTo...>, type_list<> den_to)
type_list<NumTo, NumsTo...>,
type_list<> den_to)
{ {
if constexpr (constexpr auto ext = extract_convertible_quantities(NumFrom{}, NumTo{}); ext.same_dimension) { if constexpr (constexpr auto ext = extract_convertible_quantities(NumFrom{}, NumTo{}); ext.same_dimension) {
return process_extracted<process_entities::numerators, ext>(type_list<NumsFrom...>{}, den_from, return decltype(process_extracted<process_entities::numerators, ext>(type_list<NumsFrom...>{}, den_from,
type_list<NumsTo...>{}, den_to); type_list<NumsTo...>{}, den_to)){};
} else { } else {
constexpr auto num_from_compl = decltype(get_complexity(NumFrom{}))::value; constexpr auto num_from_compl = decltype(get_complexity(NumFrom{}))::value;
constexpr auto num_to_compl = decltype(get_complexity(NumTo{}))::value; constexpr auto num_to_compl = decltype(get_complexity(NumTo{}))::value;
constexpr auto max_compl = max(num_from_compl, num_to_compl); constexpr auto max_compl = max(num_from_compl, num_to_compl);
if constexpr (max_compl > 1) { if constexpr (max_compl > 1) {
if constexpr (num_from_compl == max_compl) { if constexpr (num_from_compl == max_compl) {
constexpr auto res = explode_to_equation(NumFrom{}); constexpr auto res = decltype(explode_to_equation(NumFrom{})){};
return convertible_impl((res.equation * ... * map_power(NumsFrom{})), return decltype(convertible_impl(decltype((res.equation * ... * map_power(NumsFrom{}))){},
(map_power(NumTo{}) * ... * map_power(NumsTo{}))); decltype((map_power(NumTo{}) * ... * map_power(NumsTo{}))){})){};
} else { } else {
constexpr auto res = explode_to_equation(NumTo{}); constexpr auto res = decltype(explode_to_equation(NumTo{})){};
return min(res.result, convertible_impl((map_power(NumFrom{}) * ... * map_power(NumsFrom{})), return std::integral_constant<
(res.equation * ... * map_power(NumsTo{})))); specs_convertible_result,
min(res.result, decltype(convertible_impl(decltype((map_power(NumFrom{}) * ... * map_power(NumsFrom{}))){},
decltype((res.equation * ... * map_power(NumsTo{}))){}))::value)>{};
} }
} } else
return std::integral_constant<specs_convertible_result, specs_convertible_result::no>{};
} }
return specs_convertible_result::no;
} }
template<typename DenFrom, typename... DensFrom, typename DenTo, typename... DensTo> template<typename DenFrom, typename... DensFrom, typename DenTo, typename... DensTo>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<> num_from, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<> num_from, type_list<DenFrom, DensFrom...>,
type_list<DenFrom, DensFrom...>, type_list<> num_to, type_list<DenTo, DensTo...>)
type_list<> num_to,
type_list<DenTo, DensTo...>)
{ {
if constexpr (constexpr auto ext = extract_convertible_quantities(DenFrom{}, DenTo{}); ext.same_dimension) if constexpr (constexpr auto ext = extract_convertible_quantities(DenFrom{}, DenTo{}); ext.same_dimension)
return process_extracted<process_entities::denominators, ext>(num_from, type_list<DensFrom...>{}, num_to, return decltype(process_extracted<process_entities::denominators, ext>(num_from, type_list<DensFrom...>{}, num_to,
type_list<DensTo...>{}); type_list<DensTo...>{})){};
else { else {
constexpr auto den_from_compl = decltype(get_complexity(DenFrom{}))::value; constexpr auto den_from_compl = decltype(get_complexity(DenFrom{}))::value;
constexpr auto den_to_compl = decltype(get_complexity(DenTo{}))::value; constexpr auto den_to_compl = decltype(get_complexity(DenTo{}))::value;
constexpr auto max_compl = max(den_from_compl, den_to_compl); constexpr auto max_compl = max(den_from_compl, den_to_compl);
if constexpr (max_compl > 1) { if constexpr (max_compl > 1) {
if constexpr (den_from_compl == max_compl) { if constexpr (den_from_compl == max_compl) {
constexpr auto res = explode_to_equation(DenFrom{}); constexpr auto res = decltype(explode_to_equation(DenFrom{})){};
return convertible_impl(dimensionless / (res.equation * ... * map_power(DensFrom{})), return decltype(convertible_impl(
dimensionless / (map_power(DenTo{}) * ... * map_power(DensTo{}))); decltype(dimensionless / decltype((res.equation * ... * map_power(DensFrom{}))){}){},
decltype(dimensionless / decltype((map_power(DenTo{}) * ... * map_power(DensTo{}))){}){})){};
} else { } else {
constexpr auto res = explode_to_equation(DenTo{}); constexpr auto res = decltype(explode_to_equation(DenTo{})){};
return min(res.result, convertible_impl(dimensionless / (map_power(DenFrom{}) * ... * map_power(DensFrom{})), return std::integral_constant<
dimensionless / (res.equation * ... * map_power(DensTo{})))); specs_convertible_result,
min(res.result,
decltype(convertible_impl(
decltype(dimensionless / decltype((map_power(DenFrom{}) * ... * map_power(DensFrom{}))){}){},
decltype(dimensionless / decltype((res.equation * ... * map_power(DensTo{}))){}){}))::value)>{};
} }
} } else
return std::integral_constant<specs_convertible_result, specs_convertible_result::no>{};
} }
return specs_convertible_result::no;
} }
template<typename... NumsFrom, typename... DensFrom> template<typename... NumsFrom, typename... DensFrom>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<NumsFrom...>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<NumsFrom...>, type_list<DensFrom...>, type_list<>,
type_list<DensFrom...>, type_list<>, type_list<>)
type_list<>)
{ {
if constexpr (((... * map_power(NumsFrom{})) / (... * map_power(DensFrom{}))).dimension == dimension_one) if constexpr (decltype(decltype((... * map_power(NumsFrom{}))){} /
return specs_convertible_result::yes; decltype((... * map_power(DensFrom{}))){})::dimension == dimension_one)
return std::integral_constant<specs_convertible_result, specs_convertible_result::yes>{};
else else
return specs_convertible_result::no; return std::integral_constant<specs_convertible_result, specs_convertible_result::no>{};
} }
template<typename... NumsTo, typename... DensTo> template<typename... NumsTo, typename... DensTo>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<>, type_list<>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<>, type_list<>, type_list<NumsTo...>,
type_list<NumsTo...>, type_list<DensTo...>) type_list<DensTo...>)
{ {
if constexpr (((... * map_power(NumsTo{})) / (... * map_power(DensTo{}))).dimension == dimension_one) if constexpr (decltype(decltype((... * map_power(NumsTo{}))){} /
return specs_convertible_result::explicit_conversion; decltype((... * map_power(DensTo{}))){})::dimension == dimension_one)
return std::integral_constant<specs_convertible_result, specs_convertible_result::explicit_conversion>{};
else else
return specs_convertible_result::no; return std::integral_constant<specs_convertible_result, specs_convertible_result::no>{};
} }
template<typename... NumsFrom> template<typename... NumsFrom>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<NumsFrom...>, type_list<>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<NumsFrom...>, type_list<>, type_list<>, type_list<>)
type_list<>, type_list<>)
{ {
if constexpr ((... * map_power(NumsFrom{})).dimension == dimension_one) if constexpr (decltype((... * map_power(NumsFrom{})))::dimension == dimension_one)
return specs_convertible_result::yes; return std::integral_constant<specs_convertible_result, specs_convertible_result::yes>{};
else else
return specs_convertible_result::no; return std::integral_constant<specs_convertible_result, specs_convertible_result::no>{};
} }
template<typename... DensFrom> template<typename... DensFrom>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<>, type_list<DensFrom...>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<>, type_list<DensFrom...>, type_list<>, type_list<>)
type_list<>, type_list<>)
{ {
if constexpr ((... * map_power(DensFrom{})).dimension == dimension_one) if constexpr (decltype((... * map_power(DensFrom{})))::dimension == dimension_one)
return specs_convertible_result::yes; return std::integral_constant<specs_convertible_result, specs_convertible_result::yes>{};
else else
return specs_convertible_result::no; return std::integral_constant<specs_convertible_result, specs_convertible_result::no>{};
} }
template<typename... NumsTo> template<typename... NumsTo>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<>, type_list<>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<>, type_list<>, type_list<NumsTo...>, type_list<>)
type_list<NumsTo...>, type_list<>)
{ {
if constexpr ((... * map_power(NumsTo{})).dimension == dimension_one) if constexpr (decltype((... * map_power(NumsTo{})))::dimension == dimension_one)
return specs_convertible_result::explicit_conversion; return std::integral_constant<specs_convertible_result, specs_convertible_result::explicit_conversion>{};
else else
return specs_convertible_result::no; return std::integral_constant<specs_convertible_result, specs_convertible_result::no>{};
} }
template<typename... DensTo> template<typename... DensTo>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<>, type_list<>, type_list<>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<>, type_list<>, type_list<>, type_list<DensTo...>)
type_list<DensTo...>)
{ {
if constexpr ((... * map_power(DensTo{})).dimension == dimension_one) if constexpr (decltype((... * map_power(DensTo{})))::dimension == dimension_one)
return specs_convertible_result::explicit_conversion; return std::integral_constant<specs_convertible_result, specs_convertible_result::explicit_conversion>{};
else else
return specs_convertible_result::no; return std::integral_constant<specs_convertible_result, specs_convertible_result::no>{};
} }
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(type_list<>, type_list<>, type_list<>, [[nodiscard]] consteval auto are_ingredients_convertible(type_list<>, type_list<>, type_list<>, type_list<>)
type_list<>)
{ {
return specs_convertible_result::yes; return std::integral_constant<specs_convertible_result, specs_convertible_result::yes>{};
} }
template<DerivedQuantitySpec From, DerivedQuantitySpec To> template<DerivedQuantitySpec From, DerivedQuantitySpec To>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(From, To) [[nodiscard]] consteval auto are_ingredients_convertible(From, To)
{ {
return are_ingredients_convertible(type_list_sort<typename From::_num_, type_list_of_ingredients_less>{}, return decltype(are_ingredients_convertible(type_list_sort<typename From::_num_, type_list_of_ingredients_less>{},
type_list_sort<typename From::_den_, type_list_of_ingredients_less>{}, type_list_sort<typename From::_den_, type_list_of_ingredients_less>{},
type_list_sort<typename To::_num_, type_list_of_ingredients_less>{}, type_list_sort<typename To::_num_, type_list_of_ingredients_less>{},
type_list_sort<typename To::_den_, type_list_of_ingredients_less>{}); type_list_sort<typename To::_den_, type_list_of_ingredients_less>{})){};
} }
template<DerivedQuantitySpec From, NamedQuantitySpec To> template<DerivedQuantitySpec From, NamedQuantitySpec To>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(From, To) [[nodiscard]] consteval auto are_ingredients_convertible(From, To)
{ {
return are_ingredients_convertible(type_list_sort<typename From::_num_, type_list_of_ingredients_less>{}, return decltype(are_ingredients_convertible(type_list_sort<typename From::_num_, type_list_of_ingredients_less>{},
type_list_sort<typename From::_den_, type_list_of_ingredients_less>{}, type_list_sort<typename From::_den_, type_list_of_ingredients_less>{},
type_list<To>{}, type_list<>{}); type_list<To>{}, type_list<>{})){};
} }
template<NamedQuantitySpec From, DerivedQuantitySpec To> template<NamedQuantitySpec From, DerivedQuantitySpec To>
[[nodiscard]] consteval specs_convertible_result are_ingredients_convertible(From, To) [[nodiscard]] consteval auto are_ingredients_convertible(From, To)
{ {
return are_ingredients_convertible(type_list<From>{}, type_list<>{}, return decltype(are_ingredients_convertible(type_list<From>{}, type_list<>{},
type_list_sort<typename To::_num_, type_list_of_ingredients_less>{}, type_list_sort<typename To::_num_, type_list_of_ingredients_less>{},
type_list_sort<typename To::_den_, type_list_of_ingredients_less>{}); type_list_sort<typename To::_den_, type_list_of_ingredients_less>{})){};
} }
template<QuantitySpec From, QuantitySpec To> template<QuantitySpec From, QuantitySpec To>
[[nodiscard]] consteval specs_convertible_result convertible_kinds(From from_kind, To to_kind) [[nodiscard]] consteval auto convertible_kinds(From from_kind, To to_kind)
{ {
constexpr auto exploded_kind_result = [](specs_convertible_result res) { constexpr auto exploded_kind_result = [](specs_convertible_result res) {
using enum specs_convertible_result; using enum specs_convertible_result;
@@ -1363,75 +1392,91 @@ template<QuantitySpec From, QuantitySpec To>
}; };
if constexpr ((NamedQuantitySpec<decltype(from_kind)> && NamedQuantitySpec<decltype(to_kind)>) || if constexpr ((NamedQuantitySpec<decltype(from_kind)> && NamedQuantitySpec<decltype(to_kind)>) ||
decltype(get_complexity(from_kind))::value == decltype(get_complexity(to_kind))::value) decltype(get_complexity(from_kind))::value == decltype(get_complexity(to_kind))::value)
return convertible_impl(from_kind, to_kind); return decltype(convertible_impl(from_kind, to_kind)){};
else if constexpr (decltype(get_complexity(from_kind))::value > decltype(get_complexity(to_kind))::value) else if constexpr (decltype(get_complexity(from_kind))::value > decltype(get_complexity(to_kind))::value)
return exploded_kind_result(convertible_impl( return std::integral_constant<specs_convertible_result,
get_kind_tree_root(explode<decltype(get_complexity(to_kind))::value>(from_kind).quantity), to_kind)); exploded_kind_result(decltype(convertible_impl(
decltype(get_kind_tree_root(decltype(explode<decltype(get_complexity(
to_kind))::value>(from_kind))::quantity)){},
to_kind))::value)>{};
else else
return exploded_kind_result(convertible_impl( return std::integral_constant<
from_kind, get_kind_tree_root(explode<decltype(get_complexity(from_kind))::value>(to_kind).quantity))); specs_convertible_result,
exploded_kind_result(decltype(convertible_impl(
from_kind, decltype(get_kind_tree_root(
decltype(explode<decltype(get_complexity(from_kind))::value>(to_kind))::quantity)){}))::value)>{};
} }
template<NamedQuantitySpec From, NamedQuantitySpec To> template<NamedQuantitySpec From, NamedQuantitySpec To>
[[nodiscard]] consteval specs_convertible_result convertible_named(From from, To to) [[nodiscard]] consteval auto convertible_named(From from, To to)
{ {
using enum specs_convertible_result; using enum specs_convertible_result;
if constexpr (have_common_base(From{}, To{})) { if constexpr (decltype(have_common_base(From{}, To{}))::value) {
if constexpr (decltype(is_child_of(From{}, To{}))::value) return yes; if constexpr (decltype(is_child_of(From{}, To{}))::value)
if constexpr (decltype(is_child_of(To{}, From{}))::value) return explicit_conversion; return std::integral_constant<specs_convertible_result, yes>{};
if constexpr (get_kind(From{}) == get_kind(To{})) return cast; else if constexpr (decltype(is_child_of(To{}, From{}))::value)
return no; return std::integral_constant<specs_convertible_result, explicit_conversion>{};
else if constexpr (get_kind(From{}) == get_kind(To{}))
return std::integral_constant<specs_convertible_result, cast>{};
else
return std::integral_constant<specs_convertible_result, no>{};
} else if constexpr (get_kind(From{}) != get_kind(To{})) } else if constexpr (get_kind(From{}) != get_kind(To{}))
return no; return std::integral_constant<specs_convertible_result, no>{};
else if constexpr (decltype(get_complexity(From{}))::value != decltype(get_complexity(To{}))::value) { else if constexpr (decltype(get_complexity(From{}))::value != decltype(get_complexity(To{}))::value) {
if constexpr (decltype(get_complexity(From{}))::value > decltype(get_complexity(To{}))::value) if constexpr (decltype(get_complexity(From{}))::value > decltype(get_complexity(To{}))::value)
return convertible_impl(explode<decltype(get_complexity(to))::value>(from).quantity, to); return decltype(convertible_impl(explode<decltype(get_complexity(to))::value>(from).quantity, to)){};
else { else {
auto res = explode<decltype(get_complexity(from))::value>(to); auto res = decltype(explode<decltype(get_complexity(from))::value>(to)){};
return min(res.result, convertible_impl(from, res.quantity)); return std::integral_constant<specs_convertible_result,
min(res.result, decltype(convertible_impl(from, res.quantity))::value)>{};
} }
} }
} }
template<QuantitySpec From, QuantitySpec To> template<QuantitySpec From, QuantitySpec To>
[[nodiscard]] consteval specs_convertible_result convertible_impl(From from, To to) [[nodiscard]] consteval auto convertible_impl(From from, To to)
{ {
using enum specs_convertible_result; using enum specs_convertible_result;
// NOLINTBEGIN(bugprone-branch-clone) // NOLINTBEGIN(bugprone-branch-clone)
if constexpr (From::dimension != To::dimension) if constexpr (From::dimension != To::dimension)
return no; return std::integral_constant<specs_convertible_result, no>{};
else if constexpr (From{} == To{}) else if constexpr (From{} == To{})
return yes; return std::integral_constant<specs_convertible_result, yes>{};
else if constexpr (QuantityKindSpec<From> || QuantityKindSpec<To>) { else if constexpr (QuantityKindSpec<From> || QuantityKindSpec<To>) {
return convertible_kinds(get_kind_tree_root(from), get_kind_tree_root(to)); return decltype(convertible_kinds(decltype(get_kind_tree_root(from)){}, decltype(get_kind_tree_root(to)){})){};
} else if constexpr (NestedQuantityKindSpecOf<get_kind_tree_root(To{}), from> && get_kind_tree_root(To{}) == To{}) } else if constexpr (NestedQuantityKindSpecOf<decltype(get_kind_tree_root(To{})){}, from> &&
return yes; decltype(get_kind_tree_root(To{})){} == To{})
return std::integral_constant<specs_convertible_result, yes>{};
else if constexpr (NamedQuantitySpec<From> && NamedQuantitySpec<To>) { else if constexpr (NamedQuantitySpec<From> && NamedQuantitySpec<To>) {
return convertible_named(from, to); return decltype(convertible_named(from, to)){};
} else if constexpr (DerivedQuantitySpec<From> && DerivedQuantitySpec<To>) { } else if constexpr (DerivedQuantitySpec<From> && DerivedQuantitySpec<To>) {
return are_ingredients_convertible(from, to); return decltype(are_ingredients_convertible(from, to)){};
} else if constexpr (DerivedQuantitySpec<From>) { } else if constexpr (DerivedQuantitySpec<From>) {
auto res = explode<decltype(get_complexity(to))::value>(from); auto res = decltype(explode<decltype(get_complexity(to))::value>(from)){};
if constexpr (NamedQuantitySpec<decltype(res.quantity)>) if constexpr (NamedQuantitySpec<decltype(res.quantity)>)
return convertible_impl(res.quantity, to); return decltype(convertible_impl(res.quantity, to)){};
else if constexpr (requires { to._equation_; }) { else if constexpr (requires { to._equation_; }) {
auto eq = explode_to_equation(to); auto eq = decltype(explode_to_equation(to)){};
return min(eq.result, convertible_impl(res.quantity, eq.equation)); return std::integral_constant<specs_convertible_result,
min(eq.result, decltype(convertible_impl(res.quantity, eq.equation))::value)>{};
} else } else
return are_ingredients_convertible(from, to); return decltype(are_ingredients_convertible(from, to)){};
} else if constexpr (DerivedQuantitySpec<To>) { } else if constexpr (DerivedQuantitySpec<To>) {
auto res = explode<decltype(get_complexity(from))::value>(to); auto res = decltype(explode<decltype(get_complexity(from))::value>(to)){};
if constexpr (NamedQuantitySpec<decltype(res.quantity)>) if constexpr (NamedQuantitySpec<decltype(res.quantity)>)
return min(res.result, convertible_impl(from, res.quantity)); return std::integral_constant<specs_convertible_result,
min(res.result, decltype(convertible_impl(from, res.quantity))::value)>{};
else if constexpr (requires { from._equation_; }) else if constexpr (requires { from._equation_; })
return min(res.result, convertible_impl(from._equation_, res.quantity)); return std::integral_constant<
specs_convertible_result, min(res.result, decltype(convertible_impl(from._equation_, res.quantity))::value)>{};
else else
return min(res.result, are_ingredients_convertible(from, to)); return std::integral_constant<specs_convertible_result,
} min(res.result, decltype(are_ingredients_convertible(from, to))::value)>{};
// NOLINTEND(bugprone-branch-clone) } else
return no; // NOLINTEND(bugprone-branch-clone)
return std::integral_constant<specs_convertible_result, no>{};
} }
} // namespace detail } // namespace detail
@@ -1441,19 +1486,19 @@ MP_UNITS_EXPORT_BEGIN
template<QuantitySpec From, QuantitySpec To> template<QuantitySpec From, QuantitySpec To>
[[nodiscard]] consteval bool implicitly_convertible(From from, To to) [[nodiscard]] consteval bool implicitly_convertible(From from, To to)
{ {
return detail::convertible_impl(from, to) == detail::specs_convertible_result::yes; return decltype(detail::convertible_impl(from, to))::value == detail::specs_convertible_result::yes;
} }
template<QuantitySpec From, QuantitySpec To> template<QuantitySpec From, QuantitySpec To>
[[nodiscard]] consteval bool explicitly_convertible(From from, To to) [[nodiscard]] consteval bool explicitly_convertible(From from, To to)
{ {
return detail::convertible_impl(from, to) >= detail::specs_convertible_result::explicit_conversion; return decltype(detail::convertible_impl(from, to))::value >= detail::specs_convertible_result::explicit_conversion;
} }
template<QuantitySpec From, QuantitySpec To> template<QuantitySpec From, QuantitySpec To>
[[nodiscard]] consteval bool castable(From from, To to) [[nodiscard]] consteval bool castable(From from, To to)
{ {
return detail::convertible_impl(from, to) >= detail::specs_convertible_result::cast; return decltype(detail::convertible_impl(from, to))::value >= detail::specs_convertible_result::cast;
} }
template<QuantitySpec QS1, QuantitySpec QS2> template<QuantitySpec QS1, QuantitySpec QS2>
@@ -1497,7 +1542,7 @@ template<QuantitySpec Q>
} else if constexpr (defined_as_kind(Q{})) { } else if constexpr (defined_as_kind(Q{})) {
return q; return q;
} else if constexpr (requires { Q::_parent_; }) { } else if constexpr (requires { Q::_parent_; }) {
return get_kind_tree_root(Q::_parent_); return decltype(get_kind_tree_root(Q::_parent_)){};
} else if constexpr (detail::DerivedQuantitySpec<Q>) { } else if constexpr (detail::DerivedQuantitySpec<Q>) {
return decltype(detail::expr_map<detail::to_kind, derived_quantity_spec, struct dimensionless, return decltype(detail::expr_map<detail::to_kind, derived_quantity_spec, struct dimensionless,
detail::type_list_of_quantity_spec_less>(q)){}; detail::type_list_of_quantity_spec_less>(q)){};
@@ -1515,15 +1560,15 @@ MP_UNITS_EXPORT_BEGIN
template<QuantitySpec Q> template<QuantitySpec Q>
[[nodiscard]] consteval detail::QuantityKindSpec auto get_kind(Q q) [[nodiscard]] consteval detail::QuantityKindSpec auto get_kind(Q q)
{ {
return kind_of<detail::get_kind_tree_root(q)>; return kind_of<decltype(detail::get_kind_tree_root(q)){}>;
} }
[[nodiscard]] consteval QuantitySpec auto common_quantity_spec(QuantitySpec auto q) { return q; } [[nodiscard]] consteval QuantitySpec auto common_quantity_spec(QuantitySpec auto q) { return q; }
template<QuantitySpec Q1, QuantitySpec Q2> template<QuantitySpec Q1, QuantitySpec Q2>
[[nodiscard]] consteval QuantitySpec auto common_quantity_spec(Q1 q1, Q2 q2) [[nodiscard]] consteval QuantitySpec auto common_quantity_spec(Q1 q1, Q2 q2)
requires(implicitly_convertible(get_kind_tree_root(q1), get_kind_tree_root(q2)) || requires(implicitly_convertible(decltype(get_kind_tree_root(q1)){}, decltype(get_kind_tree_root(q2)){}) ||
implicitly_convertible(get_kind_tree_root(q2), get_kind_tree_root(q1))) implicitly_convertible(decltype(get_kind_tree_root(q2)){}, decltype(get_kind_tree_root(q1)){}))
{ {
using QQ1 = decltype(detail::remove_kind(q1)); using QQ1 = decltype(detail::remove_kind(q1));
using QQ2 = decltype(detail::remove_kind(q2)); using QQ2 = decltype(detail::remove_kind(q2));
@@ -1549,10 +1594,10 @@ template<QuantitySpec Q1, QuantitySpec Q2>
return q2; return q2;
else if constexpr (implicitly_convertible(Q2{}, Q1{})) else if constexpr (implicitly_convertible(Q2{}, Q1{}))
return q1; return q1;
else if constexpr (implicitly_convertible(get_kind_tree_root(Q1{}), get_kind_tree_root(Q2{}))) else if constexpr (implicitly_convertible(decltype(get_kind_tree_root(Q1{})){}, decltype(get_kind_tree_root(Q2{})){}))
return get_kind_tree_root(q2); return decltype(get_kind_tree_root(q2)){};
else else
return get_kind_tree_root(q1); return decltype(get_kind_tree_root(q1)){};
// NOLINTEND(bugprone-branch-clone) // NOLINTEND(bugprone-branch-clone)
} }