// The MIT License (MIT) // // Copyright (c) 2018 Mateusz Pusz // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #pragma once #include #include #include #include namespace units { template requires (R::num * R::den > 0) struct unit : downcast_base> { using dimension = D; using ratio = R; }; // is_unit namespace detail { template inline constexpr bool is_unit = false; template inline constexpr bool is_unit> = true; } template concept Unit = std::is_empty_v && detail::is_unit>; // make_derived_unit namespace detail { template struct get_unit_base_dim; template struct get_unit_base_dim> { static_assert(sizeof...(Rest) == 0, "Base unit expected"); using dimension = E::dimension; }; template struct get_ratio { using ratio = ::units::ratio<1>; }; template struct get_ratio { using unit_base_dim = get_unit_base_dim::dimension; using ratio = conditional, typename U::ratio, typename get_ratio::ratio>; }; template struct ratio_op; template struct ratio_op { using ratio = Result; }; template struct ratio_op { using calc_ratio = conditional<(UnitExpNum * UnitExpDen > 0), ratio_multiply, ratio_divide>; static constexpr int value = (UnitExpNum * UnitExpDen > 0) ? (UnitExpNum - UnitExpDen) : (UnitExpNum + UnitExpDen); using ratio = ratio_op::ratio; }; template struct derived_ratio; template struct derived_ratio, Us...> { using ratio = ::units::ratio<1>; }; template struct derived_ratio, Us...> { using rest_ratio = derived_ratio, Us...>::ratio; using e_ratio = get_ratio::ratio; using ratio = ratio_op::ratio; }; template using make_derived_unit = unit::ratio>; } namespace detail { template requires (0 <= Value) && (Value < 10) inline constexpr basic_fixed_string superscript_number = "\u2070"; // template<> inline constexpr basic_fixed_string superscript_number<0> = "\u2070"; template<> inline constexpr basic_fixed_string superscript_number<1> = "\u00b9"; template<> inline constexpr basic_fixed_string superscript_number<2> = "\u00b2"; template<> inline constexpr basic_fixed_string superscript_number<3> = "\u00b3"; template<> inline constexpr basic_fixed_string superscript_number<4> = "\u2074"; template<> inline constexpr basic_fixed_string superscript_number<5> = "\u2075"; template<> inline constexpr basic_fixed_string superscript_number<6> = "\u2076"; template<> inline constexpr basic_fixed_string superscript_number<7> = "\u2077"; template<> inline constexpr basic_fixed_string superscript_number<8> = "\u2078"; template<> inline constexpr basic_fixed_string superscript_number<9> = "\u2079"; template requires (Value >= 0) constexpr auto superscript() { if constexpr(Value < 10) return superscript_number; else return superscript() + superscript(); } template requires (Value >= 0) constexpr auto regular() { if constexpr(Value < 10) return basic_fixed_string(static_cast('0' + Value)); else return regular() + regular(); } template constexpr auto ratio_text() { if constexpr(Ratio::num != 1 || Ratio::den != 1) { auto txt = basic_fixed_string("[") + regular(); if constexpr(Ratio::den == 1) { return txt + basic_fixed_string("]"); } else { return txt + basic_fixed_string("/") + regular() + basic_fixed_string("]"); } } else { return basic_fixed_string(""); } } template constexpr auto prefix_or_ratio_text() { if constexpr(Ratio::num != 1 || Ratio::den != 1) { if constexpr (!std::same_as) { using prefix = downcast>; if constexpr(!std::same_as>) { // print as a prefixed unit return prefix::symbol; } else { // print as a ratio of the coherent unit return ratio_text(); } } else { // print as a ratio of the coherent unit return ratio_text(); } } } template constexpr auto operator_text() { if constexpr(Idx == 0) { if constexpr(Divide) { return basic_fixed_string("1/"); } else { return basic_fixed_string(""); } } else { if constexpr(Divide) { return basic_fixed_string("/"); } else { return basic_fixed_string("⋅"); } } } template constexpr auto exp_text() { // get calculation operator + symbol const auto txt = operator_text() + E::dimension::symbol; if constexpr(E::den != 1) { // add root part return txt + basic_fixed_string("^(") + regular() + basic_fixed_string("/") + regular() + basic_fixed_string(")"); } else if constexpr(abs(E::num) != 1) { // add exponent part return txt + superscript(); } else { return txt; } } template constexpr auto symbol_text_impl(dimension, std::index_sequence) { return (exp_text() + ...); } template constexpr auto symbol_text(dimension d) { return symbol_text_impl<>(d, std::index_sequence_for()); } template constexpr auto unit_text() { if constexpr(!is_unit) { // Unit is a downcasted derived unit child class already so just print defined symbol immediately return Unit::symbol; } else { // we are dealing with a non-user-defined unit here using ratio = Unit::ratio; using dim = Unit::dimension; if constexpr(!is_dimension) { // downcasted user-defined dimension // print as a prefix or ratio of a coherent unit symbol defined by the user using coherent_unit = downcast>>; return prefix_or_ratio_text() + coherent_unit::symbol; } else { // print as a ratio of a coherent unit + coherent unit dimensions and their exponents return ratio_text() + symbol_text(dim{}); } } } } // namespace detail // derived_unit template struct named_coherent_derived_unit : downcast_child>> { static constexpr auto symbol = Symbol; using prefix_type = PrefixType; }; template struct coherent_derived_unit : downcast_child>> { static constexpr auto symbol = detail::symbol_text(D()); using prefix_type = PrefixType; }; template struct named_derived_unit : downcast_child> { static constexpr auto symbol = Symbol; }; template requires requires { U::symbol; } struct prefixed_derived_unit : downcast_child>> { static constexpr auto symbol = P::symbol + U::symbol; using prefix_type = P::prefix_type; }; template struct named_deduced_derived_unit : downcast_child> { static constexpr auto symbol = Symbol; }; template struct deduced_derived_unit : downcast_child> { static constexpr auto symbol = basic_fixed_string("bbb"); }; } // namespace units