// Boost.Units - A C++ library for zero-overhead dimensional analysis and 
// unit/quantity manipulation and conversion
//
// Copyright (C) 2003-2008 Matthias Christian Schabel
// Copyright (C) 2008 Steven Watanabe
//
// 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)

#ifndef BOOST_UNITS_CONSTANTS_HPP
#define BOOST_UNITS_CONSTANTS_HPP

#include <boost/config/no_tr1/cmath.hpp>
#include <iosfwd>
#include <iomanip>

#include <boost/io/ios_state.hpp>

#include <boost/units/static_constant.hpp>
#include <boost/units/units_fwd.hpp>
#include <boost/units/operators.hpp>

namespace boost {

namespace units {

template<class Base>
struct constant 
{ 
    typedef typename Base::value_type value_type; 
    operator value_type() const    { return Base().value(); } 
    value_type value() const       { return Base().value(); } 
    value_type uncertainty() const { return Base().uncertainty(); } 
    value_type lower_bound() const { return Base().lower_bound(); } 
    value_type upper_bound() const { return Base().upper_bound(); } 
}; 

template<class Base>
struct physical_constant 
{ 
    typedef typename Base::value_type value_type; 
    operator value_type() const    { return Base().value(); } 
    value_type value() const       { return Base().value(); } 
    value_type uncertainty() const { return Base().uncertainty(); } 
    value_type lower_bound() const { return Base().lower_bound(); } 
    value_type upper_bound() const { return Base().upper_bound(); } 
}; 

#define BOOST_UNITS_DEFINE_HELPER(name, symbol, template_name)  \
                                                                \
template<class T, class Arg1, class Arg2>                       \
struct name ## _typeof_helper<constant<T>, template_name<Arg1, Arg2> >\
{                                                               \
    typedef typename name ## _typeof_helper<typename T::value_type, template_name<Arg1, Arg2> >::type type;\
};                                                              \
                                                                \
template<class T, class Arg1, class Arg2>                       \
struct name ## _typeof_helper<template_name<Arg1, Arg2>, constant<T> >\
{                                                               \
    typedef typename name ## _typeof_helper<template_name<Arg1, Arg2>, typename T::value_type>::type type;\
};                                                              \
                                                                \
template<class T, class Arg1, class Arg2>                       \
typename name ## _typeof_helper<typename T::value_type, template_name<Arg1, Arg2> >::type \
operator symbol(const constant<T>& t, const template_name<Arg1, Arg2>& u)\
{                                                               \
    return(t.value() symbol u);                                 \
}                                                               \
                                                                \
template<class T, class Arg1, class Arg2>                       \
typename name ## _typeof_helper<template_name<Arg1, Arg2>, typename T::value_type>::type \
operator symbol(const template_name<Arg1, Arg2>& u, const constant<T>& t)\
{                                                               \
    return(u symbol t.value());                                 \
}

BOOST_UNITS_DEFINE_HELPER(add, +, unit)
BOOST_UNITS_DEFINE_HELPER(add, +, quantity)
BOOST_UNITS_DEFINE_HELPER(subtract, -, unit)
BOOST_UNITS_DEFINE_HELPER(subtract, -, quantity)
BOOST_UNITS_DEFINE_HELPER(multiply, *, unit)
BOOST_UNITS_DEFINE_HELPER(multiply, *, quantity)
BOOST_UNITS_DEFINE_HELPER(divide, /, unit)
BOOST_UNITS_DEFINE_HELPER(divide, /, quantity)

#undef BOOST_UNITS_DEFINE_HELPER

#define BOOST_UNITS_DEFINE_HELPER(name, symbol)                     \
                                                                    \
template<class T1, class T2>                                        \
struct name ## _typeof_helper<constant<T1>, constant<T2> >          \
{                                                                   \
    typedef typename name ## _typeof_helper<typename T1::value_type, typename T2::value_type>::type type;\
};                                                                  \
                                                                    \
template<class T1, class T2>                                        \
typename name ## _typeof_helper<typename T1::value_type, typename T2::value_type>::type \
operator symbol(const constant<T1>& t, const constant<T2>& u)       \
{                                                                   \
    return(t.value() symbol u.value());                             \
}                                                                   \
                                                                    \
template<class T1, class T2>                                        \
struct name ## _typeof_helper<constant<T1>, T2>                     \
{                                                                   \
    typedef typename name ## _typeof_helper<typename T1::value_type, T2>::type type;\
};                                                                  \
                                                                    \
template<class T1, class T2>                                        \
struct name ## _typeof_helper<T1, constant<T2> >                    \
{                                                                   \
    typedef typename name ## _typeof_helper<T1, typename T2::value_type>::type type;\
};                                                                  \
                                                                    \
template<class T1, class T2>                                        \
typename name ## _typeof_helper<typename T1::value_type, T2>::type  \
operator symbol(const constant<T1>& t, const T2& u)                 \
{                                                                   \
    return(t.value() symbol u);                                     \
}                                                                   \
                                                                    \
template<class T1, class T2>                                        \
typename name ## _typeof_helper<T1, typename T2::value_type>::type  \
operator symbol(const T1& t, const constant<T2>& u)                 \
{                                                                   \
    return(t symbol u.value());                                     \
}

BOOST_UNITS_DEFINE_HELPER(add, +)
BOOST_UNITS_DEFINE_HELPER(subtract, -)
BOOST_UNITS_DEFINE_HELPER(multiply, *)
BOOST_UNITS_DEFINE_HELPER(divide, /)

#undef BOOST_UNITS_DEFINE_HELPER

#define BOOST_UNITS_PHYSICAL_CONSTANT(name, type, value_, uncertainty_) \
struct name ## _t {                                                     \
    typedef type value_type;                                            \
    operator value_type() const    { return value_; }                   \
    value_type value() const       { return value_; }                   \
    value_type uncertainty() const { return uncertainty_; }             \
    value_type lower_bound() const { return value_-uncertainty_; }      \
    value_type upper_bound() const { return value_+uncertainty_; }      \
};                                                                      \
BOOST_UNITS_STATIC_CONSTANT(name, boost::units::constant<boost::units::physical_constant<name ## _t> >) = { }

// stream output
template<class Char, class Traits, class Y>
inline
std::basic_ostream<Char,Traits>& operator<<(std::basic_ostream<Char,Traits>& os,const physical_constant<Y>& val)
{
    boost::io::ios_precision_saver precision_saver(os);
    //boost::io::ios_width_saver width_saver(os);
    boost::io::ios_flags_saver flags_saver(os);

    //os << std::setw(21);
    typedef typename Y::value_type value_type;
    
    if (val.uncertainty() > value_type())
    {
        const double relative_uncertainty = std::abs(val.uncertainty()/val.value());
    
        const double  exponent = std::log10(relative_uncertainty);
        const long digits_of_precision = static_cast<long>(std::ceil(std::abs(exponent)))+3;
        
        // should try to replicate NIST CODATA syntax 
        os << std::setprecision(digits_of_precision) 
           //<< std::setw(digits_of_precision+8) 
           //<< std::scientific
           << val.value();
//           << long(10*(relative_uncertainty/std::pow(Y(10),Y(exponent))));

        os << " (rel. unc. = " 
           << std::setprecision(1) 
           //<< std::setw(7) 
           << std::scientific
           << relative_uncertainty << ")";
    }
    else
    {
        os << val.value() << " (exact)";
    }
    
    return os;
}

// stream output
template<class Char, class Traits, class Y>
inline
std::basic_ostream<Char,Traits>& operator<<(std::basic_ostream<Char,Traits>& os,const constant<Y>&)
{
    os << Y();
    return os;
}

} // namespace units

} // namespace boost

#endif // BOOST_UNITS_CONSTANTS_HPP
