// 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)

/** 
\file
    
\brief complex.cpp

\details
Demonstrate a complex number class that functions correctly with quantities.

Output:
@verbatim

//[complex_output_1
+L      = 2 + 1 i m
-L      = -2 + -1 i m
L+L     = 4 + 2 i m
L-L     = 0 + 0 i m
L*L     = 3 + 4 i m^2
L/L     = 1 + 0 i dimensionless
L^3     = 2 + 11 i m^3
L^(3/2) = 2.56713 + 2.14247 i m^(3/2)
3vL     = 1.29207 + 0.201294 i m^(1/3)
(3/2)vL = 1.62894 + 0.520175 i m^(2/3)
//]

//[complex_output_2
+L      = 2 m + 1 m i
-L      = -2 m + -1 m i
L+L     = 4 m + 2 m i
L-L     = 0 m + 0 m i
L*L     = 3 m^2 + 4 m^2 i
L/L     = 1 dimensionless + 0 dimensionless i
L^3     = 2 m^3 + 11 m^3 i
L^(3/2) = 2.56713 m^(3/2) + 2.14247 m^(3/2) i
3vL     = 1.29207 m^(1/3) + 0.201294 m^(1/3) i
(3/2)vL = 1.62894 m^(2/3) + 0.520175 m^(2/3) i
//]

@endverbatim
**/

#include <cmath>
#include <complex>
#include <iostream>

#include <boost/mpl/list.hpp>

#include <boost/units/io.hpp>
#include <boost/units/pow.hpp>
#include <boost/units/quantity.hpp>

#include "test_system.hpp"

//[complex_class_snippet_1
namespace boost {

namespace units {

/// replacement complex class 
template<class T>
class complex
{
    public:
        typedef complex<T>  this_type;
        
        constexpr complex(const T& r = 0,const T& i = 0) : r_(r),i_(i) { }
        constexpr complex(const this_type& source) : r_(source.r_),i_(source.i_) { }
        
        constexpr this_type& operator=(const this_type& source)
        {
            if (this == &source) return *this;
            
            r_ = source.r_;
            i_ = source.i_;
            
            return *this;
        }
        
        constexpr T& real()             { return r_; }
        constexpr T& imag()             { return i_; }
        
        constexpr const T& real() const       { return r_; }
        constexpr const T& imag() const       { return i_; }

        constexpr this_type& operator+=(const T& val)
        {
            r_ += val;
            return *this;
        }
        
        constexpr this_type& operator-=(const T& val)
        {
            r_ -= val;
            return *this;
        }
        
        constexpr this_type& operator*=(const T& val)
        {
            r_ *= val;
            i_ *= val;
            return *this;
        }
        
        constexpr this_type& operator/=(const T& val)
        {
            r_ /= val;
            i_ /= val;
            return *this;
        }
        
        constexpr this_type& operator+=(const this_type& source)
        {
            r_ += source.r_;
            i_ += source.i_;
            return *this;
        }
        
        constexpr this_type& operator-=(const this_type& source)
        {
            r_ -= source.r_;
            i_ -= source.i_;
            return *this;
        }
        
        constexpr this_type& operator*=(const this_type& source)
        {
            *this = *this * source;
            return *this;
        }
        
        constexpr this_type& operator/=(const this_type& source)
        {
            *this = *this / source;
            return *this;
        }
        
    private:
        T   r_,i_;
};

}

}

#if BOOST_UNITS_HAS_BOOST_TYPEOF

#include BOOST_TYPEOF_INCREMENT_REGISTRATION_GROUP()

BOOST_TYPEOF_REGISTER_TEMPLATE(boost::units::complex, 1)

#endif

namespace boost {

namespace units {

template<class X>
constexpr
complex<typename unary_plus_typeof_helper<X>::type>
operator+(const complex<X>& x)
{
    typedef typename unary_plus_typeof_helper<X>::type  type;
    
    return complex<type>(x.real(),x.imag());
}

template<class X>
constexpr
complex<typename unary_minus_typeof_helper<X>::type>
operator-(const complex<X>& x)
{
    typedef typename unary_minus_typeof_helper<X>::type type;
    
    return complex<type>(-x.real(),-x.imag());
}

template<class X,class Y>
constexpr
complex<typename add_typeof_helper<X,Y>::type>
operator+(const complex<X>& x,const complex<Y>& y)
{
    typedef typename boost::units::add_typeof_helper<X,Y>::type type;
    
    return complex<type>(x.real()+y.real(),x.imag()+y.imag());
}

template<class X,class Y>
constexpr
complex<typename boost::units::subtract_typeof_helper<X,Y>::type>
operator-(const complex<X>& x,const complex<Y>& y)
{
    typedef typename boost::units::subtract_typeof_helper<X,Y>::type    type;
    
    return complex<type>(x.real()-y.real(),x.imag()-y.imag());
}

template<class X,class Y>
constexpr
complex<typename boost::units::multiply_typeof_helper<X,Y>::type>
operator*(const complex<X>& x,const complex<Y>& y)
{
    typedef typename boost::units::multiply_typeof_helper<X,Y>::type    type;
    
    return complex<type>(x.real()*y.real() - x.imag()*y.imag(),
                         x.real()*y.imag() + x.imag()*y.real());

//  fully correct implementation has more complex return type
//
//    typedef typename boost::units::multiply_typeof_helper<X,Y>::type xy_type;
//    
//    typedef typename boost::units::add_typeof_helper<
//      xy_type,xy_type>::type         xy_plus_xy_type;
//    typedef typename
//        boost::units::subtract_typeof_helper<xy_type,xy_type>::type
//        xy_minus_xy_type;
//    
//    BOOST_STATIC_ASSERT((boost::is_same<xy_plus_xy_type,
//                                       xy_minus_xy_type>::value == true));
//    
//    return complex<xy_plus_xy_type>(x.real()*y.real()-x.imag()*y.imag(),
//                                    x.real()*y.imag()+x.imag()*y.real());
}

template<class X,class Y>
constexpr
complex<typename boost::units::divide_typeof_helper<X,Y>::type>
operator/(const complex<X>& x,const complex<Y>& y)
{
    // naive implementation of complex division
    typedef typename boost::units::divide_typeof_helper<X,Y>::type type;

    return complex<type>((x.real()*y.real()+x.imag()*y.imag())/
                            (y.real()*y.real()+y.imag()*y.imag()),
                         (x.imag()*y.real()-x.real()*y.imag())/
                            (y.real()*y.real()+y.imag()*y.imag()));
                         
//  fully correct implementation has more complex return type
//
//  typedef typename boost::units::multiply_typeof_helper<X,Y>::type xy_type;
//  typedef typename boost::units::multiply_typeof_helper<Y,Y>::type yy_type;
//
//  typedef typename boost::units::add_typeof_helper<xy_type, xy_type>::type
//      xy_plus_xy_type;
//  typedef typename boost::units::subtract_typeof_helper<
//      xy_type,xy_type>::type xy_minus_xy_type;
//
//  typedef typename boost::units::divide_typeof_helper<
//      xy_plus_xy_type,yy_type>::type      xy_plus_xy_over_yy_type;
//  typedef typename boost::units::divide_typeof_helper<
//      xy_minus_xy_type,yy_type>::type     xy_minus_xy_over_yy_type;
//
//  BOOST_STATIC_ASSERT((boost::is_same<xy_plus_xy_over_yy_type,
//                                  xy_minus_xy_over_yy_type>::value == true));
//
//  return complex<xy_plus_xy_over_yy_type>(
//      (x.real()*y.real()+x.imag()*y.imag())/
//          (y.real()*y.real()+y.imag()*y.imag()),
//      (x.imag()*y.real()-x.real()*y.imag())/
//          (y.real()*y.real()+y.imag()*y.imag()));
}

template<class Y>
complex<Y> 
pow(const complex<Y>& x,const Y& y)
{
    std::complex<Y> tmp(x.real(),x.imag());
    
    tmp = std::pow(tmp,y);
    
    return complex<Y>(tmp.real(),tmp.imag());
}

template<class Y>
std::ostream& operator<<(std::ostream& os,const complex<Y>& val)
{
    os << val.real() << " + " << val.imag() << " i";

    return os;
}

/// specialize power typeof helper for complex<Y>
template<class Y,long N,long D> 
struct power_typeof_helper<complex<Y>,static_rational<N,D> >                
{ 
    typedef complex<
        typename power_typeof_helper<Y,static_rational<N,D> >::type
    > type; 
    
    static type value(const complex<Y>& x)  
    { 
        const static_rational<N,D>  rat;

        const Y    m = Y(rat.numerator())/Y(rat.denominator());
        
        return boost::units::pow(x,m);
    }
};

/// specialize root typeof helper for complex<Y>
template<class Y,long N,long D> 
struct root_typeof_helper<complex<Y>,static_rational<N,D> >                
{ 
    typedef complex<
        typename root_typeof_helper<Y,static_rational<N,D> >::type
    > type; 
    
    static type value(const complex<Y>& x)  
    { 
        const static_rational<N,D>  rat;

        const Y    m = Y(rat.denominator())/Y(rat.numerator());
        
        return boost::units::pow(x,m);
    }
};

/// specialize power typeof helper for complex<quantity<Unit,Y> >
template<class Y,class Unit,long N,long D> 
struct power_typeof_helper<complex<quantity<Unit,Y> >,static_rational<N,D> >
{ 
    typedef typename
        power_typeof_helper<Y,static_rational<N,D> >::type       value_type;
    typedef typename
        power_typeof_helper<Unit,static_rational<N,D> >::type    unit_type;
    typedef quantity<unit_type,value_type>                      quantity_type;
    typedef complex<quantity_type>                              type; 
    
    static type value(const complex<quantity<Unit,Y> >& x)  
    { 
        const complex<value_type>   tmp =
            pow<static_rational<N,D> >(complex<Y>(x.real().value(),
                                                  x.imag().value()));
        
        return type(quantity_type::from_value(tmp.real()),
                    quantity_type::from_value(tmp.imag()));
    }
};

/// specialize root typeof helper for complex<quantity<Unit,Y> >
template<class Y,class Unit,long N,long D> 
struct root_typeof_helper<complex<quantity<Unit,Y> >,static_rational<N,D> >
{ 
    typedef typename
        root_typeof_helper<Y,static_rational<N,D> >::type       value_type;
    typedef typename
        root_typeof_helper<Unit,static_rational<N,D> >::type    unit_type;
    typedef quantity<unit_type,value_type>                      quantity_type;
    typedef complex<quantity_type>                              type; 
    
    static type value(const complex<quantity<Unit,Y> >& x)  
    { 
        const complex<value_type>   tmp =
            root<static_rational<N,D> >(complex<Y>(x.real().value(),
                                                   x.imag().value()));
        
        return type(quantity_type::from_value(tmp.real()),
                   quantity_type::from_value(tmp.imag()));
    }
};

} // namespace units

} // namespace boost
//]

int main(void)
{
    using namespace boost::units;
    using namespace boost::units::test;
    
    {
    //[complex_snippet_1
    typedef quantity<length,complex<double> >     length_dimension;
        
    const length_dimension    L(complex<double>(2.0,1.0)*meters);
    //]
    
    std::cout << "+L      = " << +L << std::endl
              << "-L      = " << -L << std::endl
              << "L+L     = " << L+L << std::endl
              << "L-L     = " << L-L << std::endl
              << "L*L     = " << L*L << std::endl
              << "L/L     = " << L/L << std::endl
              << "L^3     = " << pow<3>(L) << std::endl
              << "L^(3/2) = " << pow< static_rational<3,2> >(L) << std::endl
              << "3vL     = " << root<3>(L) << std::endl
              << "(3/2)vL = " << root< static_rational<3,2> >(L) << std::endl
              << std::endl;
    }
    
    {
    //[complex_snippet_2
    typedef complex<quantity<length> >     length_dimension;
        
    const length_dimension    L(2.0*meters,1.0*meters);
    //]
    
    std::cout << "+L      = " << +L << std::endl
              << "-L      = " << -L << std::endl
              << "L+L     = " << L+L << std::endl
              << "L-L     = " << L-L << std::endl
              << "L*L     = " << L*L << std::endl
              << "L/L     = " << L/L << std::endl
              << "L^3     = " << pow<3>(L) << std::endl
              << "L^(3/2) = " << pow< static_rational<3,2> >(L) << std::endl
              << "3vL     = " << root<3>(L) << std::endl
              << "(3/2)vL = " << root< static_rational<3,2> >(L) << std::endl
              << std::endl;
    }

    return 0;
}
