IAP GITLAB

Skip to content
Snippets Groups Projects
quantity.hpp 31.04 KiB
/**
 * \file quantity.hpp
 *
 * \brief   Zero-overhead dimensional analysis and unit/quantity manipulation and conversion.
 * \author  Michael S. Kenniston, Martin Moene
 * \date    7 September 2013
 * \since   0.4
 *
 * Copyright 2013 Universiteit Leiden. All rights reserved.
 *
 * Copyright (c) 2001 by Michael S. Kenniston.  For the most
 * recent version check www.xnet.com/~msk/quantity.  Permission is granted
 * to use this code without restriction so long as this copyright
 * notice appears in all source files.
 *
 * This code is provided as-is, with no warrantee of correctness.
 *
 * Distributed under the Boost Software License, Version 1.0. (See accompanying
 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 */

/*
 * Unless otherwise specified, the definitions of all units in this
 * file are from NIST Special Publication 811, found online at
 * http://physics.nist.gov/Document/sp811.pdf
 * Other sources: OED = Oxford English Dictionary
 */

#ifndef PHYS_UNITS_QUANTITY_HPP_INCLUDED
#define PHYS_UNITS_QUANTITY_HPP_INCLUDED

#include <cmath>
#include <cstdlib>
#include <utility>  // std::declval

/// namespace phys.

namespace phys {

/// namespace units.

namespace units {

#ifdef PHYS_UNITS_REP_TYPE
   using Rep = PHYS_UNITS_REP_TYPE;
#else
   using Rep = double;
#endif

/*
 * declare now, define later.
 */
template< typename Dims, typename T = Rep >
class quantity;

/**
 * We could drag dimensions around individually, but it's much more convenient to package them.
 */
template< int D1, int D2, int D3, int D4 = 0, int D5 = 0, int D6 = 0, int D7 = 0 >
struct dimensions
{
    enum
    {
        dim1 = D1,
        dim2 = D2,
        dim3 = D3,
        dim4 = D4,
        dim5 = D5,
        dim6 = D6,
        dim7 = D7,

        is_all_zero =
            D1 == 0 && D2 == 0 && D3 == 0 && D4 == 0 && D5 == 0 && D6 == 0 && D7 == 0,

        is_base =
            1 == (D1 != 0) + (D2 != 0) + (D3 != 0) + (D4 != 0) + (D5 != 0) + (D6 != 0) + (D7 != 0)  &&
            1 ==  D1 + D2 + D3 + D4 + D5 + D6 + D7,
    };

    template< int R1, int R2, int R3, int R4, int R5, int R6, int R7 >
    constexpr bool operator==( dimensions<R1, R2, R3, R4, R5, R6, R7> const & ) const
    {
        return D1==R1 && D2==R2 && D3==R3 && D4==R4 && D5==R5 && D6==R6 && D7==R7;
    }

    template< int R1, int R2, int R3, int R4, int R5, int R6, int R7 >
    constexpr bool operator!=( dimensions<R1, R2, R3, R4, R5, R6, R7> const & rhs ) const
    {
        return !( *this == rhs );
    }
};

/// demensionless 'dimension'.

typedef dimensions< 0, 0, 0 > dimensionless_d;

/// namespace detail.

namespace detail {

/**
 * \brief The "collapse" template is used to avoid quantity< dimensions< 0, 0, 0 > >,
 * i.e. to make dimensionless results come out as type "Rep".
 */
template< typename D, typename T >
struct collapse
{
    typedef quantity< D, T > type;
};

template< typename T >
struct collapse< dimensionless_d, T >
{
    typedef T type;
};

template< typename D, typename T >
using Collapse = typename collapse<D,T>::type;

// promote types of expression to result type.

template < typename X, typename Y >
using PromoteAdd = decltype( std::declval<X>() + std::declval<Y>() );

template < typename X, typename Y >
using PromoteMul = decltype( std::declval<X>() * std::declval<Y>() );

/*
 * The following batch of structs are type generators to calculate
 * the correct type of the result of various operations.
 */

/**
 * product type generator.
 */
template< typename DX, typename DY, typename T >
struct product
{
    enum
    {
        d1 = DX::dim1 + DY::dim1,
        d2 = DX::dim2 + DY::dim2,
        d3 = DX::dim3 + DY::dim3,
        d4 = DX::dim4 + DY::dim4,
        d5 = DX::dim5 + DY::dim5,
        d6 = DX::dim6 + DY::dim6,
        d7 = DX::dim7 + DY::dim7,
    };

    typedef Collapse< dimensions< d1, d2, d3, d4, d5, d6, d7 >, T > type;
};

template< typename DX, typename DY, typename X, typename Y>
using Product = typename product<DX, DY, PromoteMul<X,Y>>::type;

/**
 * quotient type generator.
 */
template< typename DX, typename DY, typename T >
struct quotient
{
    enum
    {
        d1 = DX::dim1 - DY::dim1,
        d2 = DX::dim2 - DY::dim2,
        d3 = DX::dim3 - DY::dim3,
        d4 = DX::dim4 - DY::dim4,
        d5 = DX::dim5 - DY::dim5,
        d6 = DX::dim6 - DY::dim6,
        d7 = DX::dim7 - DY::dim7,
    };

    typedef Collapse< dimensions< d1, d2, d3, d4, d5, d6, d7 >, T > type;
};

template< typename DX, typename DY, typename X, typename Y>
using Quotient = typename quotient<DX, DY, PromoteMul<X,Y>>::type;

/**
 * reciprocal type generator.
 */
template< typename D, typename T >
struct reciprocal
{
    enum
    {
        d1 = - D::dim1,
        d2 = - D::dim2,
        d3 = - D::dim3,
        d4 = - D::dim4,
        d5 = - D::dim5,
        d6 = - D::dim6,
        d7 = - D::dim7,
    };

    typedef Collapse< dimensions< d1, d2, d3, d4, d5, d6, d7 >, T > type;
};

template< typename D, typename X, typename Y>
using Reciprocal = typename reciprocal<D, PromoteMul<X,Y>>::type;

/**
 * power type generator.
 */
template< typename D, int N, typename T >
struct power
{
    enum
    {
        d1 = N * D::dim1,
        d2 = N * D::dim2,
        d3 = N * D::dim3,
        d4 = N * D::dim4,
        d5 = N * D::dim5,
        d6 = N * D::dim6,
        d7 = N * D::dim7,
    };

    typedef Collapse< dimensions< d1, d2, d3, d4, d5, d6, d7 >, T > type;
};

template< typename D, int N, typename T >
using Power = typename detail::power< D, N, T >::type;

/**
 * root type generator.
 */
template< typename D, int N, typename T >
struct root
{
    enum
    {
        all_even_multiples =
            D::dim1 % N == 0 &&
            D::dim2 % N == 0 &&
            D::dim3 % N == 0 &&
            D::dim4 % N == 0 &&
            D::dim5 % N == 0 &&
            D::dim6 % N == 0 &&
            D::dim7 % N == 0
    };

    enum
    {
        d1 = D::dim1 / N,
        d2 = D::dim2 / N,
        d3 = D::dim3 / N,
        d4 = D::dim4 / N,
        d5 = D::dim5 / N,
        d6 = D::dim6 / N,
        d7 = D::dim7 / N
    };

    typedef Collapse< dimensions< d1, d2, d3, d4, d5, d6, d7 >, T > type;
};

template< typename D, int N, typename T >
using Root = typename detail::root< D, N, T >::type;

/**
 * tag to construct a quantity from a magnitude.
 */
constexpr struct magnitude_tag_t{} magnitude_tag{};

} // namespace detail

/**
 * \brief class "quantity" is the heart of the library. It associates
 * dimensions  with a single "Rep" data member and protects it from
 * dimensionally inconsistent use.
 */
template< typename Dims, typename T /*= Rep */ >
class quantity
{
public:
    typedef Dims dimension_type;

    typedef T value_type;

    typedef quantity<Dims, T> this_type;

    constexpr quantity() : m_value{} { }

    /**
     * public converting initializing constructor;
     * requires magnitude_tag to prevent constructing a quantity from a raw magnitude.
     */
    template <typename X>
    constexpr explicit quantity( detail::magnitude_tag_t, X x )
    : m_value( x ) { }

    /**
     * converting copy-assignment constructor.
     */
    template <typename X >
    constexpr quantity( quantity<Dims, X> const & x )
    : m_value( x.magnitude() ) { }

//    /**
//     * convert to compatible unit, for example: (3._dm).to(meter) gives 0.3;
//     */
//    constexpr value_type to( quantity const & x ) const {  return *this / x; }

    /**
     * convert to given unit, for example: (3._dm).to(meter) gives 0.3;
     */
    template <typename DX, typename X>
    constexpr auto to( quantity<DX,X> const & x ) const -> detail::Quotient<Dims,DX,T,X>
    {
        return *this / x;
    }

    /**
     * the quantity's magnitude.
     */
    constexpr value_type magnitude() const { return m_value; }

    /**
     * the quantity's dimensions.
     */
    constexpr dimension_type dimension() const { return dimension_type{}; }

    /**
     * We need a "zero" of each type -- for comparisons, to initialize running
     * totals, etc.  Note:  0 m != 0 kg, since they are of different dimensionality.
     * zero is really just defined for convenience, since
     * quantity< length_d >::zero == 0 * meter, etc.
     */
    static constexpr quantity zero() { return quantity{ value_type( 0.0 ) }; }
//    static constexpr quantity zero = quantity{ value_type( 0.0 ) };

private:
    /**
     * private initializing constructor.
     */
    constexpr explicit quantity( value_type x ) : m_value{ x } { }

private:
    value_type m_value;

    enum { has_dimension = ! Dims::is_all_zero };

    // static_assert( has_dimension, "quantity dimensions must not all be zero" ); // MR: removed

private:
    // friends:

    // arithmetic

    template <typename D, typename X, typename Y>
    friend constexpr quantity<D, X> &
    operator+=( quantity<D, X> & x, quantity<D, Y> const & y );

    template <typename D, typename X>
    friend constexpr quantity<D, X>
    operator+( quantity<D, X> const & x );

    template< typename D, typename X, typename Y >
    friend constexpr quantity <D, detail::PromoteAdd<X,Y>>
    operator+( quantity<D, X> const & x, quantity<D, Y> const & y );

    template <typename D, typename X, typename Y>
    friend constexpr quantity<D, X> &
    operator-=( quantity<D, X> & x, quantity<D, Y> const & y );

    template <typename D, typename X>
    friend constexpr quantity<D, X>
    operator-( quantity<D, X> const & x );

    template< typename D, typename X, typename Y >
    friend constexpr quantity <D, detail::PromoteAdd<X,Y>>
    operator-( quantity<D, X> const & x, quantity<D, Y> const & y );

    template< typename D, typename X, typename Y>
    friend constexpr quantity<D, X> &
    operator*=( quantity<D, X> & x, const Y & y );

    template <typename D, typename X, typename Y>
    friend constexpr quantity<D, detail::PromoteMul<X,Y>>
    operator*( quantity<D, X> const & x, const Y & y );

    template <typename D, typename X, typename Y>
    friend constexpr quantity< D, detail::PromoteMul<X,Y> >
    operator*( const X & x, quantity<D, Y> const & y );

    template <typename DX, typename DY, typename X, typename Y>
    friend constexpr detail::Product<DX, DY, X, Y>
    operator*( quantity<DX, X> const & lhs, quantity< DY, Y > const & rhs );

    template< typename D, typename X, typename Y>
    friend constexpr quantity<D, X> &
    operator/=( quantity<D, X> & x, const Y & y );

    template <typename D, typename X, typename Y>
    friend constexpr quantity<D, detail::PromoteMul<X,Y>>
    operator/( quantity<D, X> const & x, const Y & y );

    template <typename D, typename X, typename Y>
    friend constexpr detail::Reciprocal<D, X, Y>
    operator/( const X & x, quantity<D, Y> const & y );

    template <typename DX, typename DY, typename X, typename Y>
    friend constexpr detail::Quotient<DX, DY, X, Y>
    operator/( quantity<DX, X> const & x, quantity< DY, Y > const & y );

    // absolute value.

    template <typename D, typename X>
    friend constexpr quantity<D,X>
    abs( quantity<D,X> const & x );

    // powers and roots

    template <int N, typename D, typename X>
    friend detail::Power<D, N, X>
    nth_power( quantity<D, X> const & x );

    template <typename D, typename X>
    friend constexpr detail::Power<D, 2, X>
    square( quantity<D, X> const & x );

    template <typename D, typename X>
    friend constexpr detail::Power<D, 3, X>
    cube( quantity<D, X> const & x );

    template <int N, typename D, typename X>
    friend detail::Root<D, N, X>
    nth_root( quantity<D, X> const & x );

    template <typename D, typename X>
    friend detail::Root< D, 2, X >
    sqrt( quantity<D, X> const & x );

    // comparison

    template <typename D, typename X, typename Y>
    friend constexpr bool operator==( quantity<D, X> const & x, quantity<D, Y> const & y );

    template <typename D, typename X, typename Y>
    friend constexpr bool operator!=( quantity<D, X> const & x, quantity<D, Y> const & y );

    template <typename D, typename X, typename Y>
    friend constexpr bool operator<( quantity<D, X> const & x, quantity<D, Y> const & y );

    template <typename D, typename X, typename Y>
    friend constexpr bool operator<=( quantity<D, X> const & x, quantity<D, Y> const & y );

    template <typename D, typename X, typename Y>
    friend constexpr bool operator>( quantity<D, X> const & x, quantity<D, Y> const & y );

    template <typename D, typename X, typename Y>
    friend constexpr bool operator>=( quantity<D, X> const & x, quantity<D, Y> const & y );
};

// Give names to the seven fundamental dimensions of physical reality.

typedef dimensions< 1, 0, 0, 0, 0, 0, 0 > length_d;
typedef dimensions< 0, 1, 0, 0, 0, 0, 0 > mass_d;
typedef dimensions< 0, 0, 1, 0, 0, 0, 0 > time_interval_d;
typedef dimensions< 0, 0, 0, 1, 0, 0, 0 > electric_current_d;
typedef dimensions< 0, 0, 0, 0, 1, 0, 0 > thermodynamic_temperature_d;
typedef dimensions< 0, 0, 0, 0, 0, 1, 0 > amount_of_substance_d;
typedef dimensions< 0, 0, 0, 0, 0, 0, 1 > luminous_intensity_d;

// Addition operators

/// quan += quan

template <typename D, typename X, typename Y>
constexpr quantity<D, X> &
operator+=( quantity<D, X> & x, quantity<D, Y> const & y )
{
    return x.m_value += y.m_value, x;
}

/// + quan

template <typename D, typename X>
constexpr quantity<D, X>
operator+( quantity<D, X> const & x )
{
   return quantity<D, X >( +x.m_value );
}

/// quan + quan

template< typename D, typename X, typename Y >
constexpr quantity <D, detail::PromoteAdd<X,Y>>
operator+( quantity<D, X> const & x, quantity<D, Y> const & y )
{
   return quantity<D, detail::PromoteAdd<X,Y>>( x.m_value + y.m_value );
}

// Subtraction operators

/// quan -= quan

template <typename D, typename X, typename Y>
constexpr quantity<D, X> &
operator-=( quantity<D, X> & x, quantity<D, Y> const & y )
{
    return x.m_value -= y.m_value, x;
}

/// - quan

template <typename D, typename X>
constexpr quantity<D, X>
operator-( quantity<D, X> const & x )
{
   return quantity<D, X >( -x.m_value );
}

/// quan - quan

template< typename D, typename X, typename Y >
constexpr quantity <D, detail::PromoteAdd<X,Y>>
operator-( quantity<D, X> const & x, quantity<D, Y> const & y )
{
   return quantity<D, detail::PromoteAdd<X,Y>>( x.m_value - y.m_value );
}

// Multiplication operators

/// quan *= num

template< typename D, typename X, typename Y>
constexpr quantity<D, X> &
operator*=( quantity<D, X> & x, const Y & y )
{
   return x.m_value *= y, x;
}

/// quan * num

template <typename D, typename X, typename Y>
constexpr quantity<D, detail::PromoteMul<X,Y>>
operator*( quantity<D, X> const & x, const Y & y )
{
   return quantity<D, detail::PromoteMul<X,Y>>( x.m_value * y );
}

/// num * quan

template <typename D, typename X, typename Y>
constexpr quantity< D, detail::PromoteMul<X,Y> >
operator*( const X & x, quantity<D, Y> const & y )
{
   return quantity<D, detail::PromoteMul<X,Y>>( x * y.m_value );
}

/// quan * quan:

template <typename DX, typename DY, typename X, typename Y>
constexpr detail::Product<DX, DY, X, Y>
operator*( quantity<DX, X> const & lhs, quantity< DY, Y > const & rhs )
{
    return detail::Product<DX, DY, X, Y>( lhs.m_value * rhs.m_value );
}
// Division operators

/// quan /= num

template< typename D, typename X, typename Y>
constexpr quantity<D, X> &
operator/=( quantity<D, X> & x, const Y & y )
{
   return x.m_value /= y, x;
}

/// quan / num

template <typename D, typename X, typename Y>
constexpr quantity<D, detail::PromoteMul<X,Y>>
operator/( quantity<D, X> const & x, const Y & y )
{
   return quantity<D, detail::PromoteMul<X,Y>>( x.m_value / y );
}

/// num / quan

template <typename D, typename X, typename Y>
constexpr detail::Reciprocal<D, X, Y>
operator/( const X & x, quantity<D, Y> const & y )
{
   return detail::Reciprocal<D, X, Y>( x / y.m_value );
}

/// quan / quan:

template <typename DX, typename DY, typename X, typename Y>
constexpr detail::Quotient<DX, DY, X, Y>
operator/( quantity<DX, X> const & x, quantity< DY, Y > const & y )
{
    return detail::Quotient<DX, DY, X, Y>( x.m_value / y.m_value );
}

/// absolute value.

template <typename D, typename X>
constexpr quantity<D,X> abs( quantity<D,X> const & x )
{
   return quantity<D,X>( std::abs( x.m_value ) );
}

// General powers

/// N-th power.

template <int N, typename D, typename X>
detail::Power<D, N, X>
nth_power( quantity<D, X> const & x )
{
   return detail::Power<D, N, X>( std::pow( x.m_value, X( N ) ) );
}

// Low powers defined separately for efficiency.

/// square.

template <typename D, typename X>
constexpr detail::Power<D, 2, X>
square( quantity<D, X> const & x )
{
   return x * x;
}

/// cube.
template <typename D, typename X>
constexpr detail::Power<D, 3, X>
cube( quantity<D, X> const & x )
{
   return x * x * x;
}

// General root

/// n-th root.

template <int N, typename D, typename X>
detail::Root<D, N, X>
nth_root( quantity<D, X> const & x )
{
   static_assert( detail::root<D, N, X>::all_even_multiples, "root result dimensions must be integral" );

   return detail::Root<D, N, X>( std::pow( x.m_value, X( 1.0 ) / N ) );
}

// Low roots defined separately for convenience.

/// square root.

template <typename D, typename X>
detail::Root< D, 2, X >
sqrt( quantity<D, X> const & x )
{
   static_assert(
      detail::root<D, 2, X >::all_even_multiples, "root result dimensions must be integral" );

   return detail::Root<D, 2, X>( std::pow( x.m_value, X( 1.0 ) / 2 ) );
}

// Comparison operators

/// equality.

template <typename D, typename X, typename Y>
constexpr bool
operator==( quantity<D, X> const & x, quantity<D, Y> const & y )
{
   return x.m_value == y.m_value;
}

/// inequality.

template <typename D, typename X, typename Y>
constexpr bool
operator!=( quantity<D, X> const & x, quantity<D, Y> const & y )
{
   return x.m_value != y.m_value;
}

/// less-than.

template <typename D, typename X, typename Y>
constexpr bool
operator<( quantity<D, X> const & x, quantity<D, Y> const & y )
{
   return x.m_value < y.m_value;
}

/// less-equal.

template <typename D, typename X, typename Y>
constexpr bool
operator<=( quantity<D, X> const & x, quantity<D, Y> const & y )
{
   return x.m_value <= y.m_value;
}

/// greater-than.

template <typename D, typename X, typename Y>
constexpr bool
operator>( quantity<D, X> const & x, quantity<D, Y> const & y )
{
   return x.m_value > y.m_value;
}

/// greater-equal.

template <typename D, typename X, typename Y>
constexpr bool
operator>=( quantity<D, X> const & x, quantity<D, Y> const & y )
{
   return x.m_value >= y.m_value;
}

/// quantity's dimension.

template <typename DX, typename X>
inline constexpr DX dimension( quantity<DX,X> const & q ) { return q.dimension(); }

/// quantity's magnitude.

template <typename DX, typename X>
inline constexpr X magnitude( quantity<DX,X> const & q ) { return q.magnitude(); }

// The seven SI base units.  These tie our numbers to the real world.

constexpr quantity<length_d                   > meter   { detail::magnitude_tag, 1.0 };
constexpr quantity<mass_d                     > kilogram{ detail::magnitude_tag, 1.0 };
constexpr quantity<time_interval_d            > second  { detail::magnitude_tag, 1.0 };
constexpr quantity<electric_current_d         > ampere  { detail::magnitude_tag, 1.0 };
constexpr quantity<thermodynamic_temperature_d> kelvin  { detail::magnitude_tag, 1.0 };
constexpr quantity<amount_of_substance_d      > mole    { detail::magnitude_tag, 1.0 };
constexpr quantity<luminous_intensity_d       > candela { detail::magnitude_tag, 1.0 };

// The standard SI prefixes.

constexpr long double yotta = 1e+24L;
constexpr long double zetta = 1e+21L;
constexpr long double   exa = 1e+18L;
constexpr long double  peta = 1e+15L;
constexpr long double  tera = 1e+12L;
constexpr long double  giga = 1e+9L;
constexpr long double  mega = 1e+6L;
constexpr long double  kilo = 1e+3L;
constexpr long double hecto = 1e+2L;
constexpr long double  deka = 1e+1L;
constexpr long double  deci = 1e-1L;
constexpr long double centi = 1e-2L;
constexpr long double milli = 1e-3L;
constexpr long double micro = 1e-6L;
constexpr long double  nano = 1e-9L;
constexpr long double  pico = 1e-12L;
constexpr long double femto = 1e-15L;
constexpr long double  atto = 1e-18L;
constexpr long double zepto = 1e-21L;
constexpr long double yocto = 1e-24L;

// Binary prefixes, pending adoption.

constexpr long double kibi = 1024;
constexpr long double mebi = 1024 * kibi;
constexpr long double gibi = 1024 * mebi;
constexpr long double tebi = 1024 * gibi;
constexpr long double pebi = 1024 * tebi;
constexpr long double exbi = 1024 * pebi;
constexpr long double zebi = 1024 * exbi;
constexpr long double yobi = 1024 * zebi;

// The rest of the standard dimensional types, as specified in SP811.

using absorbed_dose_d             = dimensions< 2, 0, -2 >;
using absorbed_dose_rate_d        = dimensions< 2, 0, -3 >;
using acceleration_d              = dimensions< 1, 0, -2 >;
using activity_of_a_nuclide_d     = dimensions< 0, 0, -1 >;
using angular_velocity_d          = dimensions< 0, 0, -1 >;
using angular_acceleration_d      = dimensions< 0, 0, -2 >;
using area_d                      = dimensions< 2, 0, 0 >;
using capacitance_d               = dimensions< -2, -1, 4, 2 >;
using concentration_d             = dimensions< -3, 0, 0, 0, 0, 1 >;
using current_density_d           = dimensions< -2, 0, 0, 1 >;
using dose_equivalent_d           = dimensions< 2, 0, -2 >;
using dynamic_viscosity_d         = dimensions< -1, 1, -1 >;
using electric_charge_d           = dimensions< 0, 0, 1, 1 >;
using electric_charge_density_d   = dimensions< -3, 0, 1, 1 >;
using electric_conductance_d      = dimensions< -2, -1, 3, 2 >;
using electric_field_strenth_d    = dimensions< 1, 1, -3, -1 >;
using electric_flux_density_d     = dimensions< -2, 0, 1, 1 >;
using electric_potential_d        = dimensions< 2, 1, -3, -1 >;
using electric_resistance_d       = dimensions< 2, 1, -3, -2 >;
using energy_d                    = dimensions< 2, 1, -2 >;
using energy_density_d            = dimensions< -1, 1, -2 >;
using exposure_d                  = dimensions< 0, -1, 1, 1 >;
using force_d                     = dimensions< 1, 1, -2 >;
using frequency_d                 = dimensions< 0, 0, -1 >;
using heat_capacity_d             = dimensions< 2, 1, -2, 0, -1 >;
using heat_density_d              = dimensions< 0, 1, -2 >;
using heat_density_flow_rate_d    = dimensions< 0, 1, -3 >;
using heat_flow_rate_d            = dimensions< 2, 1, -3 >;
using heat_flux_density_d         = dimensions< 0, 1, -3 >;
using heat_transfer_coefficient_d = dimensions< 0, 1, -3, 0, -1 >;
using illuminance_d               = dimensions< -2, 0, 0, 0, 0, 0, 1 >;
using inductance_d                = dimensions< 2, 1, -2, -2 >;
using irradiance_d                = dimensions< 0, 1, -3 >;
using kinematic_viscosity_d       = dimensions< 2, 0, -1 >;
using luminance_d                 = dimensions< -2, 0, 0, 0, 0, 0, 1 >;
using luminous_flux_d             = dimensions< 0, 0, 0, 0, 0, 0, 1 >;
using magnetic_field_strength_d   = dimensions< -1, 0, 0, 1 >;
using magnetic_flux_d             = dimensions< 2, 1, -2, -1 >;
using magnetic_flux_density_d     = dimensions< 0, 1, -2, -1 >;
using magnetic_permeability_d     = dimensions< 1, 1, -2, -2 >;
using mass_density_d              = dimensions< -3, 1, 0 >;
using mass_flow_rate_d            = dimensions< 0, 1, -1 >;
using molar_energy_d              = dimensions< 2, 1, -2, 0, 0, -1 >;
using molar_entropy_d             = dimensions< 2, 1, -2, -1, 0, -1 >;
using moment_of_force_d           = dimensions< 2, 1, -2 >;
using permittivity_d              = dimensions< -3, -1, 4, 2 >;
using power_d                     = dimensions< 2, 1, -3 >;
using pressure_d                  = dimensions< -1, 1, -2 >;
using radiance_d                  = dimensions< 0, 1, -3 >;
using radiant_intensity_d         = dimensions< 2, 1, -3 >;
using speed_d                     = dimensions< 1, 0, -1 >;
using specific_energy_d           = dimensions< 2, 0, -2 >;
using specific_heat_capacity_d    = dimensions< 2, 0, -2, 0, -1 >;
using specific_volume_d           = dimensions< 3, -1, 0 >;
using substance_permeability_d    = dimensions< -1, 0, 1 >;
using surface_tension_d           = dimensions< 0, 1, -2 >;
using thermal_conductivity_d      = dimensions< 1, 1, -3, 0, -1 >;
using thermal_diffusivity_d       = dimensions< 2, 0, -1 >;
using thermal_insulance_d         = dimensions< 0, -1, 3, 0, 1 >;
using thermal_resistance_d        = dimensions< -2, -1, 3, 0, 1 >;
using thermal_resistivity_d       = dimensions< -1, -1, 3, 0, 1 >;
using torque_d                    = dimensions< 2, 1, -2 >;
using volume_d                    = dimensions< 3, 0, 0 >;
using volume_flow_rate_d          = dimensions< 3, 0, -1 >;
using wave_number_d               = dimensions< -1, 0, 0 >;

// Handy values.

constexpr Rep pi      { Rep( 3.141592653589793238462L ) };
constexpr Rep percent { Rep( 1 ) / 100 };

//// Not approved for use alone, but needed for use with prefixes.
constexpr quantity< mass_d                  > gram         { kilogram / 1000 };

// The derived SI units, as specified in SP811.

constexpr Rep                                 radian       { Rep( 1 ) };
constexpr Rep                                 steradian    { Rep( 1 ) };
constexpr quantity< force_d                 > newton       { meter * kilogram / square( second ) };
constexpr quantity< pressure_d              > pascal       { newton / square( meter ) };
constexpr quantity< energy_d                > joule        { newton * meter };
constexpr quantity< power_d                 > watt         { joule / second };
constexpr quantity< electric_charge_d       > coulomb      { second * ampere };
constexpr quantity< electric_potential_d    > volt         { watt / ampere };
constexpr quantity< capacitance_d           > farad        { coulomb / volt };
constexpr quantity< electric_resistance_d   > ohm          { volt / ampere };
constexpr quantity< electric_conductance_d  > siemens      { ampere / volt };
constexpr quantity< magnetic_flux_d         > weber        { volt * second };
constexpr quantity< magnetic_flux_density_d > tesla        { weber / square( meter ) };
constexpr quantity< inductance_d            > henry        { weber / ampere };
constexpr quantity< thermodynamic_temperature_d > degree_celsius   { kelvin };
constexpr quantity< luminous_flux_d         > lumen        { candela * steradian };
constexpr quantity< illuminance_d           > lux          { lumen / meter / meter };
constexpr quantity< activity_of_a_nuclide_d > becquerel    { 1 / second };
constexpr quantity< absorbed_dose_d         > gray         { joule / kilogram };
constexpr quantity< dose_equivalent_d       > sievert      { joule / kilogram };
constexpr quantity< frequency_d             > hertz        { 1 / second };

// The rest of the units approved for use with SI, as specified in SP811.
// (However, use of these units is generally discouraged.)

constexpr quantity< length_d                > angstrom     { Rep( 1e-10L ) * meter };
constexpr quantity< area_d                  > are          { Rep( 1e+2L ) * square( meter ) };
constexpr quantity< pressure_d              > bar          { Rep( 1e+5L ) * pascal };
constexpr quantity< area_d                  > barn         { Rep( 1e-28L ) * square( meter ) };
constexpr quantity< activity_of_a_nuclide_d > curie        { Rep( 3.7e+10L ) * becquerel };
constexpr quantity< time_interval_d         > day          { Rep( 86400L ) * second };
constexpr Rep                                 degree_angle { pi / 180 };
constexpr quantity< acceleration_d          > gal          { Rep( 1e-2L ) * meter / square( second ) };
constexpr quantity< area_d                  > hectare      { Rep( 1e+4L ) * square( meter ) };
constexpr quantity< time_interval_d         > hour         { Rep( 3600 ) * second };
constexpr quantity< speed_d                 > knot         { Rep( 1852 ) / 3600 * meter / second };
constexpr quantity< volume_d                > liter        { Rep( 1e-3L ) * cube( meter ) };
constexpr quantity< time_interval_d         > minute       { Rep( 60 ) * second };
constexpr Rep                                 minute_angle { pi / 10800 };
constexpr quantity< length_d                > mile_nautical{ Rep( 1852 ) * meter };
constexpr quantity< absorbed_dose_d         > rad          { Rep( 1e-2L ) * gray };
constexpr quantity< dose_equivalent_d       > rem          { Rep( 1e-2L ) * sievert };
constexpr quantity< exposure_d              > roentgen     { Rep( 2.58e-4L ) * coulomb / kilogram };
constexpr Rep                                 second_angle { pi / 648000L };
constexpr quantity< mass_d                  > ton_metric   { Rep( 1e+3L ) * kilogram };

// Alternate (non-US) spellings:

constexpr quantity< length_d                > metre        { meter };
constexpr quantity< volume_d                > litre        { liter };
constexpr Rep                                 deca         { deka };
constexpr quantity< mass_d                  > tonne        { ton_metric };

// cooked literals for base units;
// these could also have been created with a script.

#define QUANTITY_DEFINE_SCALING_LITERAL( sfx, dim, factor ) \
    constexpr quantity<dim, double> operator "" _ ## sfx(unsigned long long x) \
    { \
        return quantity<dim, double>( detail::magnitude_tag, factor * x ); \
    } \
    constexpr quantity<dim, double> operator "" _ ## sfx(long double x) \
    { \
        return quantity<dim, double>( detail::magnitude_tag, factor * x ); \
    }

#define QUANTITY_DEFINE_SCALING_LITERALS( pfx, dim, fact ) \
    QUANTITY_DEFINE_SCALING_LITERAL( Y ## pfx, dim, fact * yotta ) \
    QUANTITY_DEFINE_SCALING_LITERAL( Z ## pfx, dim, fact * zetta ) \
    QUANTITY_DEFINE_SCALING_LITERAL( E ## pfx, dim, fact * exa   ) \
    QUANTITY_DEFINE_SCALING_LITERAL( P ## pfx, dim, fact * peta  ) \
    QUANTITY_DEFINE_SCALING_LITERAL( T ## pfx, dim, fact * tera  ) \
    QUANTITY_DEFINE_SCALING_LITERAL( G ## pfx, dim, fact * giga  ) \
    QUANTITY_DEFINE_SCALING_LITERAL( M ## pfx, dim, fact * mega  ) \
    QUANTITY_DEFINE_SCALING_LITERAL( k ## pfx, dim, fact * kilo  ) \
    QUANTITY_DEFINE_SCALING_LITERAL( h ## pfx, dim, fact * hecto ) \
    QUANTITY_DEFINE_SCALING_LITERAL( da## pfx, dim, fact * deka  ) \
    QUANTITY_DEFINE_SCALING_LITERAL(      pfx, dim, fact * 1     ) \
    QUANTITY_DEFINE_SCALING_LITERAL( d ## pfx, dim, fact * deci  ) \
    QUANTITY_DEFINE_SCALING_LITERAL( c ## pfx, dim, fact * centi ) \
    QUANTITY_DEFINE_SCALING_LITERAL( m ## pfx, dim, fact * milli ) \
    QUANTITY_DEFINE_SCALING_LITERAL( u ## pfx, dim, fact * micro ) \
    QUANTITY_DEFINE_SCALING_LITERAL( n ## pfx, dim, fact * nano  ) \
    QUANTITY_DEFINE_SCALING_LITERAL( p ## pfx, dim, fact * pico  ) \
    QUANTITY_DEFINE_SCALING_LITERAL( f ## pfx, dim, fact * femto ) \
    QUANTITY_DEFINE_SCALING_LITERAL( a ## pfx, dim, fact * atto  ) \
    QUANTITY_DEFINE_SCALING_LITERAL( z ## pfx, dim, fact * zepto ) \
    QUANTITY_DEFINE_SCALING_LITERAL( y ## pfx, dim, fact * yocto )


#define QUANTITY_DEFINE_LITERALS( pfx, dim ) \
    QUANTITY_DEFINE_SCALING_LITERALS( pfx, dim, 1 )

/// literals

namespace literals {

QUANTITY_DEFINE_SCALING_LITERALS( g, mass_d, 1e-3 )

QUANTITY_DEFINE_LITERALS( m  , length_d )
QUANTITY_DEFINE_LITERALS( s  , time_interval_d )
QUANTITY_DEFINE_LITERALS( A  , electric_current_d )
QUANTITY_DEFINE_LITERALS( K  , thermodynamic_temperature_d )
QUANTITY_DEFINE_LITERALS( mol, amount_of_substance_d )
QUANTITY_DEFINE_LITERALS( cd , luminous_intensity_d )

} // namespace literals

}} // namespace phys::units

#endif // PHYS_UNITS_QUANTITY_HPP_INCLUDED

/*
 * end of file
 */