text: // 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) 2007-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_CMATH_HPP 
  #define BOOST_UNITS_CMATH_HPP
  
  #include <boost/config/no_tr1/cmath.hpp>
  #include <cstdlib>
  
  #include <boost/math/special_functions/fpclassify.hpp>
  #include <boost/math/special_functions/hypot.hpp>
  #include <boost/math/special_functions/next.hpp>
  #include <boost/math/special_functions/round.hpp>
  #include <boost/math/special_functions/sign.hpp>
  
  #include <boost/units/dimensionless_quantity.hpp>
  #include <boost/units/pow.hpp>
  #include <boost/units/quantity.hpp>
  #include <boost/units/detail/cmath_impl.hpp>
  
  #include <boost/units/systems/si/plane_angle.hpp>
  
  /// \file 
  /// \brief Overloads of functions in \<cmath\> for quantities
  ///
  /// \detailed Only functions for which a dimensionally-correct result type  
  /// can be determined are overloaded. All functions work with dimensionless
  /// quantities.
  
  // BOOST_PREVENT_MACRO_SUBSTITUTION is needed on certain compilers that define 
  // some <cmath> functions as macros; it is used for all functions even though it
  // isn't necessary -- I didn't want to think :)
  //
  // the form using namespace detail; return(f(x)); is used
  // to enable ADL for UDTs
  
  namespace boost {
  
  namespace units { 
  
  template<class Unit,class Y>
  inline 
  bool 
  isfinite BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q)
  { 
      using boost::math::isfinite;
      return isfinite BOOST_PREVENT_MACRO_SUBSTITUTION (q.value());
  }
  
  template<class Unit,class Y>
  inline 
  bool 
  isinf BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q)
  { 
      using boost::math::isinf;
      return isinf BOOST_PREVENT_MACRO_SUBSTITUTION (q.value());
  }
  
  template<class Unit,class Y>
  inline
  bool 
  isnan BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q)
  { 
      using boost::math::isnan;
      return isnan BOOST_PREVENT_MACRO_SUBSTITUTION (q.value());
  }
  
  template<class Unit,class Y>
  inline 
  bool 
  isnormal BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q)
  { 
      using boost::math::isnormal;
      return isnormal BOOST_PREVENT_MACRO_SUBSTITUTION (q.value());
  }
  
  template<class Unit,class Y>
  inline 
  bool 
  isgreater BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q1,
                                              const quantity<Unit,Y>& q2)
  { 
      using namespace detail;
      return isgreater BOOST_PREVENT_MACRO_SUBSTITUTION (q1.value(),q2.value());
  }
  
  template<class Unit,class Y>
  inline 
  bool 
  isgreaterequal BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q1,
                                                   const quantity<Unit,Y>& q2)
  { 
      using namespace detail;
      return isgreaterequal BOOST_PREVENT_MACRO_SUBSTITUTION (q1.value(),q2.value());
  }
  
  template<class Unit,class Y>
  inline 
  bool 
  isless BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q1,
                                           const quantity<Unit,Y>& q2)
  { 
      using namespace detail;
      return isless BOOST_PREVENT_MACRO_SUBSTITUTION (q1.value(),q2.value());
  }
  
  template<class Unit,class Y>
  inline 
  bool 
  islessequal BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q1,
                                                const quantity<Unit,Y>& q2)
  { 
      using namespace detail;
      return islessequal BOOST_PREVENT_MACRO_SUBSTITUTION (q1.value(),q2.value());
  }
  
  template<class Unit,class Y>
  inline 
  bool 
  islessgreater BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q1,
                                                  const quantity<Unit,Y>& q2)
  { 
      using namespace detail;
      return islessgreater BOOST_PREVENT_MACRO_SUBSTITUTION (q1.value(),q2.value());
  }
  
  template<class Unit,class Y>
  inline 
  bool 
  isunordered BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q1,
                                                const quantity<Unit,Y>& q2)
  { 
      using namespace detail;
      return isunordered BOOST_PREVENT_MACRO_SUBSTITUTION (q1.value(),q2.value());
  }
  
  template<class Unit,class Y>
  inline 
  quantity<Unit,Y> 
  abs BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q)
  {
      using std::abs;
  
      typedef quantity<Unit,Y>    quantity_type;
      
      return quantity_type::from_value(abs BOOST_PREVENT_MACRO_SUBSTITUTION (q.value()));
  }
  
  template<class Unit,class Y>
  inline 
  quantity<Unit,Y> 
  ceil BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q)
  {
      using std::ceil;
  
      typedef quantity<Unit,Y>    quantity_type;
      
      return quantity_type::from_value(ceil BOOST_PREVENT_MACRO_SUBSTITUTION (q.value()));
  }
  
  template<class Unit,class Y>
  inline 
  quantity<Unit,Y> 
  copysign BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q1,
           const quantity<Unit,Y>& q2)
  {
      using boost::math::copysign;
  
      typedef quantity<Unit,Y>    quantity_type;
      
      return quantity_type::from_value(copysign BOOST_PREVENT_MACRO_SUBSTITUTION (q1.value(),q2.value()));
  }
  
  template<class Unit,class Y>
  inline 
  quantity<Unit,Y> 
  fabs BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q)
  {
      using std::fabs;
  
      typedef quantity<Unit,Y>    quantity_type;
      
      return quantity_type::from_value(fabs BOOST_PREVENT_MACRO_SUBSTITUTION (q.value()));
  }
  
  template<class Unit,class Y>
  inline 
  quantity<Unit,Y> 
  floor BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q)
  {
      using std::floor;
  
      typedef quantity<Unit,Y>    quantity_type;
      
      return quantity_type::from_value(floor BOOST_PREVENT_MACRO_SUBSTITUTION (q.value()));
  }
  
  template<class Unit,class Y>
  inline 
  quantity<Unit,Y> 
  fdim BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q1,
                                         const quantity<Unit,Y>& q2)
  {
      using namespace detail;
  
      typedef quantity<Unit,Y>    quantity_type;
      
      return quantity_type::from_value(fdim BOOST_PREVENT_MACRO_SUBSTITUTION (q1.value(),q2.value()));
  }
  
  #if 0
  
  template<class Unit1,class Unit2,class Unit3,class Y>
  inline 
  typename add_typeof_helper<
      typename multiply_typeof_helper<quantity<Unit1,Y>,
                                      quantity<Unit2,Y> >::type,
      quantity<Unit3,Y> >::type 
  fma BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit1,Y>& q1,
                                        const quantity<Unit2,Y>& q2,
                                        const quantity<Unit3,Y>& q3)
  {
      using namespace detail;
  
      typedef quantity<Unit1,Y>   type1;
      typedef quantity<Unit2,Y>   type2;
      typedef quantity<Unit3,Y>   type3;
      
      typedef typename multiply_typeof_helper<type1,type2>::type  prod_type;
      typedef typename add_typeof_helper<prod_type,type3>::type   quantity_type;
      
      return quantity_type::from_value(fma BOOST_PREVENT_MACRO_SUBSTITUTION (q1.value(),q2.value(),q3.value()));
  }
  
  #endif
  
  template<class Unit,class Y>
  inline 
  quantity<Unit,Y> 
  fmax BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q1,
                                         const quantity<Unit,Y>& q2)
  {
      using namespace detail;
  
      typedef quantity<Unit,Y>    quantity_type;
      
      return quantity_type::from_value(fmax BOOST_PREVENT_MACRO_SUBSTITUTION (q1.value(),q2.value()));
  }
  
  template<class Unit,class Y>
  inline 
  quantity<Unit,Y> 
  fmin BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q1,
                                         const quantity<Unit,Y>& q2)
  {
      using namespace detail;
  
      typedef quantity<Unit,Y>    quantity_type;
      
      return quantity_type::from_value(fmin BOOST_PREVENT_MACRO_SUBSTITUTION (q1.value(),q2.value()));
  }
  
  template<class Unit,class Y>
  inline 
  int 
  fpclassify BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q)
  { 
      using boost::math::fpclassify;
  
      return fpclassify BOOST_PREVENT_MACRO_SUBSTITUTION (q.value());
  }
  
  template<class Unit,class Y>
  inline 
  typename root_typeof_helper<
      typename add_typeof_helper<
          typename power_typeof_helper<quantity<Unit,Y>,
                                       static_rational<2> >::type,
          typename power_typeof_helper<quantity<Unit,Y>,
                                       static_rational<2> >::type>::type,
          static_rational<2> >::type
  hypot BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q1,const quantity<Unit,Y>& q2)
  {
      using boost::math::hypot;
  
      typedef quantity<Unit,Y>    type1;
      
      typedef typename power_typeof_helper<type1,static_rational<2> >::type   pow_type;
      typedef typename add_typeof_helper<pow_type,pow_type>::type             add_type;
      typedef typename root_typeof_helper<add_type,static_rational<2> >::type quantity_type;
          
      return quantity_type::from_value(hypot BOOST_PREVENT_MACRO_SUBSTITUTION (q1.value(),q2.value()));
  }
  
  // does ISO C++ support long long? g++ claims not
  //template<class Unit,class Y>
  //inline 
  //quantity<Unit,long long> 
  //llrint BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q)
  //{
  //    using namespace detail;
  //
  //    typedef quantity<Unit,long long>    quantity_type;
  //    
  //    return quantity_type::from_value(llrint BOOST_PREVENT_MACRO_SUBSTITUTION (q.value()));
  //}
  
  // does ISO C++ support long long? g++ claims not
  //template<class Unit,class Y>
  //inline 
  //quantity<Unit,long long> 
  //llround BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q)
  //{
  //    using namespace detail;
  //
  //    typedef quantity<Unit,long long>    quantity_type;
  //    
  //    return quantity_type::from_value(llround BOOST_PREVENT_MACRO_SUBSTITUTION (q.value()));
  //}
  
  #if 0
  
  template<class Unit,class Y>
  inline 
  quantity<Unit,Y> 
  nearbyint BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q)
  {
      using namespace detail;
  
      typedef quantity<Unit,Y>    quantity_type;
      
      return quantity_type::from_value(nearbyint BOOST_PREVENT_MACRO_SUBSTITUTION (q.value()));
  }
  
  #endif
  
  template<class Unit,class Y>
  inline 
  quantity<Unit,Y> nextafter BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q1,
                                                               const quantity<Unit,Y>& q2)
  {
      using boost::math::nextafter;
  
      typedef quantity<Unit,Y>    quantity_type;
      
      return quantity_type::from_value(nextafter BOOST_PREVENT_MACRO_SUBSTITUTION (q1.value(),q2.value()));
  }
  template<class Unit,class Y>
  inline 
  quantity<Unit,Y> nexttoward BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q1,
                                                                const quantity<Unit,Y>& q2)
  {
      // the only difference between nextafter and nexttowards is
      // in the argument types.  Since we are requiring identical
      // argument types, there is no difference.
      using boost::math::nextafter;
  
      typedef quantity<Unit,Y>    quantity_type;
      
      return quantity_type::from_value(nextafter BOOST_PREVENT_MACRO_SUBSTITUTION (q1.value(),q2.value()));
  }
  
  #if 0
  
  template<class Unit,class Y>
  inline 
  quantity<Unit,Y> 
  rint BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q)
  {
      using namespace detail;
  
      typedef quantity<Unit,Y>    quantity_type;
      
      return quantity_type::from_value(rint BOOST_PREVENT_MACRO_SUBSTITUTION (q.value()));
  }
  
  #endif
  
  template<class Unit,class Y>
  inline 
  quantity<Unit,Y> 
  round BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q)
  {
      using boost::math::round;
  
      typedef quantity<Unit,Y>    quantity_type;
      
      return quantity_type::from_value(round BOOST_PREVENT_MACRO_SUBSTITUTION (q.value()));
  }
  
  template<class Unit,class Y>
  inline 
  int 
  signbit BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q)
  { 
      using boost::math::signbit;
  
      return signbit BOOST_PREVENT_MACRO_SUBSTITUTION (q.value());
  }
  
  template<class Unit,class Y>
  inline 
  quantity<Unit,Y> 
  trunc BOOST_PREVENT_MACRO_SUBSTITUTION (const quantity<Unit,Y>& q)
  {
      using namespace detail;
  
      typedef quantity<Unit,Y>    quantity_type;
      
      return quantity_type::from_value(trunc BOOST_PREVENT_MACRO_SUBSTITUTION (q.value()));
  }
  
  template<class Unit,class Y>
  inline
  quantity<Unit, Y>
  fmod(const quantity<Unit,Y>& q1, const quantity<Unit,Y>& q2)
  {
      using std::fmod;
      
      typedef quantity<Unit,Y> quantity_type;
  
      return quantity_type::from_value(fmod(q1.value(), q2.value()));
  }
  
  template<class Unit, class Y>
  inline
  quantity<Unit, Y>
  modf(const quantity<Unit, Y>& q1, quantity<Unit, Y>* q2)
  {
      using std::modf;
  
      typedef quantity<Unit,Y> quantity_type;
  
      return quantity_type::from_value(modf(q1.value(), &quantity_cast<Y&>(*q2)));
  }
  
  template<class Unit, class Y, class Int>
  inline
  quantity<Unit, Y>
  frexp(const quantity<Unit, Y>& q,Int* ex)
  {
      using std::frexp;
  
      typedef quantity<Unit,Y> quantity_type;
  
      return quantity_type::from_value(frexp(q.value(),ex));
  }
  
  /// For non-dimensionless quantities, integral and rational powers 
  /// and roots can be computed by @c pow<Ex> and @c root<Rt> respectively.
  template<class S, class Y>
  inline
  quantity<BOOST_UNITS_DIMENSIONLESS_UNIT(S), Y>
  pow(const quantity<BOOST_UNITS_DIMENSIONLESS_UNIT(S), Y>& q1,
      const quantity<BOOST_UNITS_DIMENSIONLESS_UNIT(S), Y>& q2)
  {
      using std::pow;
  
      typedef quantity<BOOST_UNITS_DIMENSIONLESS_UNIT(S),Y> quantity_type;
  
      return quantity_type::from_value(pow(q1.value(), q2.value()));
  }
  
  template<class S, class Y>
  inline
  quantity<BOOST_UNITS_DIMENSIONLESS_UNIT(S), Y>
  exp(const quantity<BOOST_UNITS_DIMENSIONLESS_UNIT(S), Y>& q)
  {
      using std::exp;
  
      typedef quantity<BOOST_UNITS_DIMENSIONLESS_UNIT(S), Y> quantity_type;
  
      return quantity_type::from_value(exp(q.value()));
  }
  
  template<class Unit, class Y, class Int>
  inline
  quantity<Unit, Y>
  ldexp(const quantity<Unit, Y>& q,const Int& ex)
  {
      using std::ldexp;
  
      typedef quantity<Unit,Y> quantity_type;
  
      return quantity_type::from_value(ldexp(q.value(), ex));
  }
  
  template<class S, class Y>
  inline
  quantity<BOOST_UNITS_DIMENSIONLESS_UNIT(S), Y>
  log(const quantity<BOOST_UNITS_DIMENSIONLESS_UNIT(S), Y>& q)
  {
      using std::log;
  
      typedef quantity<BOOST_UNITS_DIMENSIONLESS_UNIT(S), Y> quantity_type;
  
      return quantity_type::from_value(log(q.value()));
  }
  
  template<class S, class Y>
  inline
  quantity<BOOST_UNITS_DIMENSIONLESS_UNIT(S), Y>
  log10(const quantity<BOOST_UNITS_DIMENSIONLESS_UNIT(S), Y>& q)
  {
      using std::log10;
  
      typedef quantity<BOOST_UNITS_DIMENSIONLESS_UNIT(S), Y> quantity_type;
  
      return quantity_type::from_value(log10(q.value()));
  }
  
  template<class Unit,class Y>
  inline 
  typename root_typeof_helper<
                              quantity<Unit,Y>,
                              static_rational<2>
                             >::type
  sqrt(const quantity<Unit,Y>& q)
  {
      using std::sqrt;
  
      typedef typename root_typeof_helper<
                                          quantity<Unit,Y>,
                                          static_rational<2>
                                         >::type quantity_type;
  
      return quantity_type::from_value(sqrt(q.value()));
  }
  
  } // namespace units
  
  } // namespace boost
  
  namespace boost {
  
  namespace units {
  
  // trig functions with si argument/return types
  
  /// cos of theta in radians
  template<class Y>
  typename dimensionless_quantity<si::system,Y>::type 
  cos(const quantity<si::plane_angle,Y>& theta)
  {
      using std::cos;
      return cos(theta.value());
  }
  
  /// sin of theta in radians
  template<class Y>
  typename dimensionless_quantity<si::system,Y>::type 
  sin(const quantity<si::plane_angle,Y>& theta)
  {
      using std::sin;
      return sin(theta.value());
  }
  
  /// tan of theta in radians
  template<class Y>
  typename dimensionless_quantity<si::system,Y>::type 
  tan(const quantity<si::plane_angle,Y>& theta)
  {
      using std::tan;
      return tan(theta.value());
  }
  
  /// cos of theta in other angular units 
  template<class System,class Y>
  typename dimensionless_quantity<System,Y>::type 
  cos(const quantity<unit<plane_angle_dimension,System>,Y>& theta)
  {
      return cos(quantity<si::plane_angle,Y>(theta));
  }
  
  /// sin of theta in other angular units 
  template<class System,class Y>
  typename dimensionless_quantity<System,Y>::type 
  sin(const quantity<unit<plane_angle_dimension,System>,Y>& theta)
  {
      return sin(quantity<si::plane_angle,Y>(theta));
  }
  
  /// tan of theta in other angular units 
  template<class System,class Y>
  typename dimensionless_quantity<System,Y>::type 
  tan(const quantity<unit<plane_angle_dimension,System>,Y>& theta)
  {
      return tan(quantity<si::plane_angle,Y>(theta));
  }
  
  /// acos of dimensionless quantity returning angle in same system
  template<class Y,class System>
  quantity<unit<plane_angle_dimension, homogeneous_system<System> >,Y>
  acos(const quantity<unit<dimensionless_type, homogeneous_system<System> >,Y>& val)
  {
      using std::acos;
      return quantity<unit<plane_angle_dimension, homogeneous_system<System> >,Y>(acos(val.value())*si::radians);
  }
  
  /// asin of dimensionless quantity returning angle in same system
  template<class Y,class System>
  quantity<unit<plane_angle_dimension, homogeneous_system<System> >,Y>
  asin(const quantity<unit<dimensionless_type, homogeneous_system<System> >,Y>& val)
  {
      using std::asin;
      return quantity<unit<plane_angle_dimension, homogeneous_system<System> >,Y>(asin(val.value())*si::radians);
  }
  
  /// atan of dimensionless quantity returning angle in same system
  template<class Y,class System>
  quantity<unit<plane_angle_dimension, homogeneous_system<System> >,Y>
  atan(const quantity<unit<dimensionless_type, homogeneous_system<System> >, Y>& val)
  {
      using std::atan;
      return quantity<unit<plane_angle_dimension, homogeneous_system<System> >,Y>(atan(val.value())*si::radians);
  }
  
  /// atan2 of @c value_type returning angle in radians
  template<class Y, class Dimension, class System>
  quantity<unit<plane_angle_dimension, homogeneous_system<System> >, Y>
  atan2(const quantity<unit<Dimension, homogeneous_system<System> >, Y>& y,
        const quantity<unit<Dimension, homogeneous_system<System> >, Y>& x)
  {
      using std::atan2;
      return quantity<unit<plane_angle_dimension, homogeneous_system<System> >, Y>(atan2(y.value(),x.value())*si::radians);
  }
  
  } // namespace units
  
  } // namespace boost
  
  #endif // BOOST_UNITS_CMATH_HPP
  