2018-09-29 18:08:02 -07:00
// 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
2019-04-09 21:31:09 +01:00
#include <units/dimension.h>
2019-11-04 06:37:50 +00:00
#include <units/prefix.h>
2019-05-28 16:17:48 +02:00
#include <units/ratio.h>
#include <ratio>
2018-09-29 18:08:02 -07:00
2019-09-18 21:55:16 -06:00
namespace units {
2018-09-29 18:08:02 -07:00
2019-10-17 10:16:44 +02:00
template < Dimension D , Ratio R >
2019-09-09 23:02:07 +02:00
requires ( R :: num * R :: den > 0 )
2019-07-18 23:24:24 +02:00
struct unit : downcast_base < unit < D , R >> {
2018-09-29 18:08:02 -07:00
using dimension = D ;
using ratio = R ;
};
// is_unit
namespace detail {
template < typename T >
2018-11-10 17:09:11 -08:00
inline constexpr bool is_unit = false ;
2018-09-29 18:08:02 -07:00
2019-08-08 22:31:22 +02:00
template < typename D , typename R >
2018-11-10 17:09:11 -08:00
inline constexpr bool is_unit < unit < D , R >> = true ;
2018-09-29 18:08:02 -07:00
}
template < typename T >
2019-09-28 19:10:06 +02:00
concept Unit =
2018-11-15 05:27:16 +01:00
std :: is_empty_v < T > &&
2019-09-20 18:36:14 +02:00
detail :: is_unit < downcast_base_t < T >> ;
2018-09-29 18:08:02 -07:00
2019-11-10 00:16:41 +00:00
// deduced_derived_unit
2019-04-11 13:36:06 +01:00
namespace detail {
2019-08-08 22:31:22 +02:00
template < typename D >
2019-04-11 13:36:06 +01:00
struct get_unit_base_dim ;
2019-08-08 22:31:22 +02:00
template < typename E , typename ... Rest >
2019-04-11 13:36:06 +01:00
struct get_unit_base_dim < dimension < E , Rest ... >> {
static_assert ( sizeof ...( Rest ) == 0 , "Base unit expected" );
2019-09-18 06:36:52 -06:00
using dimension = E :: dimension ;
2019-04-11 13:36:06 +01:00
};
2019-09-10 21:20:32 +02:00
template < typename Result , int UnitExpNum , int UnitExpDen , typename UnitRatio >
2019-04-11 13:36:06 +01:00
struct ratio_op ;
2019-09-10 21:20:32 +02:00
template < typename Result , int UnitExpDen , typename UnitRatio >
struct ratio_op < Result , 0 , UnitExpDen , UnitRatio > {
2019-04-11 13:36:06 +01:00
using ratio = Result ;
};
2019-09-10 21:20:32 +02:00
template < typename Result , int UnitExpNum , int UnitExpDen , typename UnitRatio >
2019-04-11 13:36:06 +01:00
struct ratio_op {
2019-09-10 21:20:32 +02:00
using calc_ratio = conditional < ( UnitExpNum * UnitExpDen > 0 ), ratio_multiply < Result , UnitRatio > ,
2019-05-28 16:21:28 +02:00
ratio_divide < Result , UnitRatio >> ;
2019-09-10 21:20:32 +02:00
static constexpr int value = ( UnitExpNum * UnitExpDen > 0 ) ? ( UnitExpNum - UnitExpDen ) : ( UnitExpNum + UnitExpDen );
using ratio = ratio_op < calc_ratio , value , UnitExpDen , UnitRatio >:: ratio ;
2019-04-11 13:36:06 +01:00
};
2019-08-08 22:31:22 +02:00
template < typename D , typename ... Us >
2019-04-11 13:36:06 +01:00
struct derived_ratio ;
2019-08-08 22:31:22 +02:00
template < typename ... Us >
2019-04-11 13:36:06 +01:00
struct derived_ratio < dimension <> , Us ... > {
2019-09-18 21:55:16 -06:00
using ratio = :: units :: ratio < 1 > ;
2019-04-11 13:36:06 +01:00
};
2019-11-10 16:55:16 +00:00
template < typename E , typename ... ERest , typename U , typename ... URest >
struct derived_ratio < dimension < E , ERest ... > , U , URest ... > {
static_assert ( same_dim < typename E :: dimension , typename U :: dimension > , "The order and number of units in `deduced_derived_unit<Us...>` should match dimensions provided in a `derived_dimension<>`" );
static_assert ( sizeof ...( ERest ) == sizeof ...( URest ), "The number of `deduced_derived_unit<Us...>` units should match the number of exponents provided to `derived_dimension<>`" );
using rest_ratio = derived_ratio < dimension < ERest ... > , URest ... >:: ratio ;
using ratio = ratio_op < rest_ratio , E :: num , E :: den , typename U :: ratio >:: ratio ;
2019-04-11 13:36:06 +01:00
};
2019-11-10 16:55:16 +00:00
template < typename ... Es >
constexpr auto exp_count ( dimension < Es ... > )
{
return sizeof ...( Es );
}
template < typename U >
inline constexpr bool is_unit_of_base_dimension = ( exp_count ( typename U :: dimension :: base_type ()) == 1 );
template < Unit ... Us >
inline constexpr bool are_units_of_base_dimension = ( is_unit_of_base_dimension < Us > && ...);
2019-09-26 22:30:57 +02:00
template < Dimension D , Unit ... Us >
2019-11-10 16:55:16 +00:00
using deduced_derived_unit =
unit < D , typename detail :: derived_ratio < std :: conditional_t < are_units_of_base_dimension < Us ... > ,
typename D :: base_type , typename D :: recipe > , Us ... >:: ratio > ;
2019-09-26 22:30:57 +02:00
2019-11-05 20:05:11 +00:00
template < int Value >
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 < int Value >
requires ( Value >= 0 )
constexpr auto superscript ()
{
if constexpr ( Value < 10 )
return superscript_number < Value > ;
else
return superscript < Value / 10 > () + superscript < Value % 10 > ();
}
template < int Value >
requires ( Value >= 0 )
constexpr auto regular ()
{
if constexpr ( Value < 10 )
return basic_fixed_string ( static_cast < char > ( '0' + Value ));
else
return regular < Value / 10 > () + regular < Value % 10 > ();
}
template < typename Ratio >
constexpr auto ratio_text ()
{
if constexpr ( Ratio :: num != 1 || Ratio :: den != 1 ) {
auto txt = basic_fixed_string ( "[" ) + regular < Ratio :: num > ();
if constexpr ( Ratio :: den == 1 ) {
return txt + basic_fixed_string ( "]" );
}
else {
return txt + basic_fixed_string ( "/" ) + regular < Ratio :: den > () + basic_fixed_string ( "]" );
}
}
else {
return basic_fixed_string ( "" );
}
}
template < typename Ratio , typename PrefixType >
constexpr auto prefix_or_ratio_text ()
{
if constexpr ( Ratio :: num != 1 || Ratio :: den != 1 ) {
if constexpr ( ! std :: same_as < PrefixType , no_prefix > ) {
using prefix = downcast < detail :: prefix_base < PrefixType , Ratio >> ;
if constexpr ( ! std :: same_as < prefix , prefix_base < PrefixType , Ratio >> ) {
// print as a prefixed unit
return prefix :: symbol ;
}
else {
// print as a ratio of the coherent unit
return ratio_text < Ratio > ();
}
}
else {
// print as a ratio of the coherent unit
return ratio_text < Ratio > ();
}
}
}
template < bool Divide , std :: size_t Idx >
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 ( "⋅" );
}
}
}
2019-11-10 00:16:41 +00:00
template < typename E , basic_fixed_string Symbol , std :: size_t Idx >
2019-11-05 20:05:11 +00:00
constexpr auto exp_text ()
{
// get calculation operator + symbol
2019-11-10 00:16:41 +00:00
const auto txt = operator_text < E :: num < 0 , Idx > () + Symbol ;
2019-11-05 20:05:11 +00:00
if constexpr ( E :: den != 1 ) {
// add root part
return txt + basic_fixed_string ( "^(" ) + regular < abs ( E :: num ) > () + basic_fixed_string ( "/" ) + regular < E :: den > () + basic_fixed_string ( ")" );
}
else if constexpr ( abs ( E :: num ) != 1 ) {
// add exponent part
return txt + superscript < abs ( E :: num ) > ();
}
else {
return txt ;
}
}
2019-11-10 19:51:25 +01:00
template < typename Dim >
constexpr auto dimension_symbol ()
{
if constexpr ( BaseDimension < Dim > )
return Dim :: symbol ;
else
// coherent derived unit
return downcast < unit < Dim , ratio < 1 >>>:: symbol ;
}
2019-11-05 20:05:11 +00:00
template < typename ... Es , std :: size_t ... Idxs >
2019-11-10 00:16:41 +00:00
constexpr auto base_symbol_text_impl ( dimension < Es ... > , std :: index_sequence < Idxs ... > )
2019-11-05 20:05:11 +00:00
{
2019-11-10 19:51:25 +01:00
return ( exp_text < Es , dimension_symbol < typename Es :: dimension > (), Idxs > () + ...);
2019-11-05 20:05:11 +00:00
}
template < typename ... Es >
2019-11-10 00:16:41 +00:00
constexpr auto base_symbol_text ( dimension < Es ... > d )
2019-11-05 20:05:11 +00:00
{
2019-11-10 00:16:41 +00:00
return base_symbol_text_impl ( d , std :: index_sequence_for < Es ... > ());
}
2019-11-10 19:51:25 +01:00
template < typename ... Es >
constexpr bool all_named ( dimension < Es ... > )
{
return ( downcast < unit < typename Es :: dimension , ratio < 1 >>>:: is_named && ...);
}
template < typename Dim >
constexpr auto base_symbol_text ()
{
using recipe = typename Dim :: recipe ;
if constexpr ( all_named ( recipe ()))
return base_symbol_text ( recipe ());
else
return base_symbol_text ( Dim ());
}
2019-11-10 00:16:41 +00:00
template < typename E , typename U , std :: size_t Idx >
constexpr auto exp_validate_and_text ()
{
2019-11-10 16:55:16 +00:00
static_assert ( same_dim < typename E :: dimension , typename U :: dimension > , "The order and number of units in `deduced_derived_unit<Us...>` should match dimensions provided in a `derived_dimension<>`" );
2019-11-10 00:16:41 +00:00
return exp_text < E , U :: symbol , Idx > ();
}
template < typename ... Us , typename ... Es , std :: size_t ... Idxs >
constexpr auto deduced_symbol_text_impl ( dimension < Es ... > , std :: index_sequence < Idxs ... > )
{
return ( exp_validate_and_text < Es , Us , Idxs > () + ...);
}
template < typename ... Us , typename ... Es >
constexpr auto deduced_symbol_text ( dimension < Es ... > d )
{
2019-11-10 16:55:16 +00:00
static_assert ( sizeof ...( Es ) == sizeof ...( Us ), "The number of `deduced_derived_unit<Us...>` units should match the number of exponents provided to `derived_dimension<>`" );
2019-11-10 00:16:41 +00:00
return deduced_symbol_text_impl < Us ... > ( d , std :: index_sequence_for < Es ... > ());
2019-11-05 20:05:11 +00:00
}
2019-11-10 16:55:16 +00:00
template < typename Dim , typename ... Us >
constexpr auto deduced_symbol_text ()
{
if constexpr ( are_units_of_base_dimension < Us ... > )
return deduced_symbol_text < Us ... > ( typename Dim :: base_type ());
else
return deduced_symbol_text < Us ... > ( typename Dim :: recipe ());
}
2019-11-05 20:05:11 +00:00
template < typename Unit >
constexpr auto unit_text ()
{
if constexpr ( ! is_unit < 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 < dim > ) {
// downcasted user-defined dimension
// print as a prefix or ratio of a coherent unit symbol defined by the user
using coherent_unit = downcast < units :: unit < dim , units :: ratio < 1 >>> ;
return prefix_or_ratio_text < ratio , typename coherent_unit :: prefix_type > () + coherent_unit :: symbol ;
}
else {
// print as a ratio of a coherent unit + coherent unit dimensions and their exponents
2019-11-10 00:16:41 +00:00
return ratio_text < ratio > () + base_symbol_text ( dim {});
2019-11-05 20:05:11 +00:00
}
}
}
} // namespace detail
// derived_unit
2019-10-17 10:16:44 +02:00
2019-11-08 16:51:45 +00:00
template < typename Child , Dimension Dim , basic_fixed_string Symbol , PrefixType PT >
struct named_coherent_derived_unit : downcast_child < Child , unit < Dim , ratio < 1 >>> {
2019-11-10 19:51:25 +01:00
static constexpr bool is_named = true ;
2019-10-18 15:34:46 +02:00
static constexpr auto symbol = Symbol ;
2019-11-08 16:51:45 +00:00
using prefix_type = PT ;
2019-10-17 10:16:44 +02:00
};
2019-11-08 16:51:45 +00:00
template < typename Child , Dimension Dim >
struct coherent_derived_unit : downcast_child < Child , unit < Dim , ratio < 1 >>> {
2019-11-10 19:51:25 +01:00
static constexpr bool is_named = false ;
static constexpr auto symbol = detail :: base_symbol_text < Dim > ();
2019-11-08 16:51:45 +00:00
using prefix_type = no_prefix ;
2019-11-04 06:37:50 +00:00
};
2019-11-08 16:51:45 +00:00
template < typename Child , Dimension Dim , basic_fixed_string Symbol , Ratio R , PrefixType PT = no_prefix >
struct named_scaled_derived_unit : downcast_child < Child , unit < Dim , R >> {
2019-11-10 19:51:25 +01:00
static constexpr bool is_named = true ;
2019-10-18 15:34:46 +02:00
static constexpr auto symbol = Symbol ;
2019-11-08 16:51:45 +00:00
using prefix_type = PT ;
};
template < typename Child , Dimension Dim , basic_fixed_string Symbol , PrefixType PT , Unit U , Unit ... Us >
2019-11-10 00:16:41 +00:00
struct named_deduced_derived_unit : downcast_child < Child , detail :: deduced_derived_unit < Dim , U , Us ... >> {
2019-11-10 19:51:25 +01:00
static constexpr bool is_named = true ;
2019-11-08 16:51:45 +00:00
static constexpr auto symbol = Symbol ;
using prefix_type = PT ;
};
template < typename Child , Dimension Dim , Unit U , Unit ... Us >
2019-11-10 19:51:25 +01:00
requires U :: is_named && ( Us :: is_named && ... && true )
2019-11-10 00:16:41 +00:00
struct deduced_derived_unit : downcast_child < Child , detail :: deduced_derived_unit < Dim , U , Us ... >> {
2019-11-10 19:51:25 +01:00
static constexpr bool is_named = false ;
2019-11-10 16:55:16 +00:00
static constexpr auto symbol = detail :: deduced_symbol_text < Dim , U , Us ... > ();
2019-11-08 16:51:45 +00:00
using prefix_type = no_prefix ;
2019-10-15 11:32:59 +02:00
};
2019-09-26 22:30:57 +02:00
2019-10-18 15:34:46 +02:00
template < typename Child , Prefix P , Unit U >
2019-11-08 16:51:45 +00:00
requires ( ! std :: same_as < typename U :: prefix_type , no_prefix > )
2019-11-02 13:54:43 +01:00
struct prefixed_derived_unit : downcast_child < Child , unit < typename U :: dimension , ratio_multiply < typename P :: ratio , typename U :: ratio >>> {
2019-11-10 19:51:25 +01:00
static constexpr bool is_named = true ;
2019-10-18 15:34:46 +02:00
static constexpr auto symbol = P :: symbol + U :: symbol ;
using prefix_type = P :: prefix_type ;
2019-10-15 11:32:59 +02:00
};
2019-09-26 22:30:57 +02:00
2019-09-18 21:55:16 -06:00
} // namespace units