// os_num_complex header.
// Copyright (c) 2003-2026 Recursion Software LLC  All rights reserved.
// Email: sales@recursionsw.com, psupport@recursionsw.com
// Last Modified: $Date$

#ifndef OS_MATH_NUMCOMPLEX_H
#define OS_MATH_NUMCOMPLEX_H

#include <ospace/common/throw.h>
#include <ospace/math/math_ex.h>



/**
 ***********************************************************
 * Template Class for Numeric Complex Data.
 ***********************************************************
 */


template< class T >
class os_num_complex
  {
  public:
    /**
     * Constructor. Constructs a complex number with real
     * and imaginary parts assigned to 0.
     */
    os_num_complex();

    /**
     * Constructor. Constructs a
     * complex number with input real and imaginary parts
     * @param re real par
     * @param im imaginary part
     */
    os_num_complex( const T& re, const T& im );

    /**
     * Constructor. Constructs
     * a complex number with input null-terminated string.
     * @param str string of the form <real>+<imag>i, no
     * white spaces
     */
    os_num_complex( const char* str );

    /**
     * Copy constructor. Constructs a complex number with
     * input complex number.
     * @param cmplx complex number
     */
    os_num_complex( const os_num_complex< T >& cmplx );

    /**
     * Access the real part of complex number.
     * @return real part
     */
    T real() const;

    /**
     * Modify the real part of complex number.
     */
    void real( const T& re );

    /**
     * Access the imaginary part of complex number.
     * @return imaginary part
     */
    T imag() const;

    /**
     * Modify the imaginary part of complex number.
     */
    void imag( const T& imag );

    /**
     * Dump contents of numeric complex to output stream.
     * @param stream reference to output stream
     */
    OS_STD_IO os_ostream& print
      (
      OS_STD_IO os_ostream& stream
      ) const;

    /**
     * Assignment operator.
     * @param rhs input complex number
     * @return reference to self
     */
    os_num_complex< T >& operator=( const os_num_complex< T >&  rhs );

    /**
     * Addition operator. Adds input complex number to
     * self.
     * @param rhs input complex number
     * @return reference to self
     */
    os_num_complex< T >& operator+=( const os_num_complex< T >&  rhs );

    /**
     * Subtraction operator. Subtracts input complex
     * number from self.
     * @param rhs input complex number
     * @return reference to self
     */
    os_num_complex< T >& operator-=( const os_num_complex< T >&  rhs );

    /**
     * Multiplication operator. Multiplies input complex
     * number with self.
     * @param rhs input complex number
     * @return reference to self
     */
    os_num_complex< T >& operator*=( const os_num_complex< T >&  rhs );

    /**
     * Division operator. Divides self with input complex
     * number.
     * @param rhs input complex number
     * @return reference to self
     */
    os_num_complex< T >& operator/=( const os_num_complex< T >&  rhs );

    /**
     * Assignment from input value. Assigns from input
     * value.
     * @param rhs input complex number
     * @return reference to self
     */
    os_num_complex< T >& operator=( const T& rhs );

    /**
     * Addition with input real value. Adds input
     * real value to self real value. The
     * imaginary value is not manipulated.
     * @param rhs input real value
     * @return reference to self
     */
    os_num_complex< T >& operator+=( const T& rhs );

    /**
     * Subtraction from input real value. Subtracts input
     * real value from self real value.
     * The imaginary value is not manipulated.
     * @param rhs input real value
     * @return reference to self
     */
    os_num_complex< T >& operator-=( const T& rhs );

    /**
     * Multiplication with input real value. Multiplies
     * input real value with self real
     * value. The imaginary value is not manipulated.
     * @param rhs input real value
     * @return reference to self
     */
    os_num_complex< T >& operator*=( const T& rhs );

    /**
     * Division from input real value. Divides self real
     * value with input real value. The
     * imaginary value is not manipulated.
     * @param rhs input real value
     * @return reference to self
     */
    os_num_complex< T >& operator/=( const T& rhs );

    /**
     * Equality operator. Compares the input complex
     * number with self. If both real and imaginary are
     * equal, returns true, else returns false.
     * @param rhs input complex number
     * @return true if equal, false if not
     */
    bool operator==( const os_num_complex< T >& rhs ) const;

    /**
     * Inequality operator. Does the reverse of Equality
     * operator.
     * @param rhs input complex number
     * @return true if equal, false if not
     */
    bool operator!=( const os_num_complex< T >& rhs ) const;

  private:
    T m_real_;
    T m_imaginary_;
  };





/**
 * Complex Number type with integer real and imaginary
 * parts.
 */
typedef os_num_complex< int > os_int_complex;


/**
 * Complex Number type with unsigned integer real and
 * imaginary parts.
 */
typedef os_num_complex< unsigned int > os_uint_complex;


/**
 * Complex Number type with long real and imaginary
 * parts.
 */
typedef os_num_complex< long > os_long_complex;


/**
 * Complex Number type with unsigned long real and
 * imaginary parts.
 */
typedef os_num_complex< unsigned long > os_ulong_complex;


/**
 * Complex Number type with float real and imaginary
 * parts.
 */
typedef os_num_complex< float > os_float_complex;


/**
 * Complex Number type with double real and imaginary
 * parts.
 */
typedef os_num_complex< double > os_double_complex;





/**
 * Complex Numbers addition binary operator. Adds input
 * complex number with input complex number.
 * @param lhs input complex number
 * @param rhs input complex number
 * @return complex sum of the lhs and rhs
 */

template< class T >
os_num_complex< T >
operator+( const os_num_complex< T >& lhs, const os_num_complex< T >& rhs );





/**
 * Complex Numbers addition binary operator. Adds input
 * complex number with input real value. The imaginary
 * value is not manipulated.
 * @param lhs input complex number
 * @param rhs input real value
 * @return complex sum of the lhs and rhs
 */

template< class T >
os_num_complex< T >
operator+( const os_num_complex< T >& lhs, const T& rhs );





/**
 * Complex Numbers addition binary operator. Adds input
 * real value with input complex number. The imaginary
 * value is not manipulated.
 * @param lhs input complex number
 * @param rhs input real value
 * @return complex sum of the lhs and rhs
 */

template< class T >
os_num_complex< T >
operator+( const T& lhs, const os_num_complex< T >& rhs );





/**
 * Complex Numbers subtraction binary operator. Subtracts
 * input complex number from input complex number.
 * @param lhs input complex number
 * @param rhs input complex number
 * @return complex sum of the lhs and rhs
 */

template< class T >
os_num_complex< T >
operator-( const os_num_complex< T >& lhs, const os_num_complex< T >& rhs );





/**
 * Complex Numbers subtraction binary operator. Subtracts
 * input real value from input complex number. The
 * imaginary value is not manipulated.
 * @param lhs input real value
 * @param rhs input complex number
 * @return complex sum of the lhs and rhs
 */

template< class T >
os_num_complex< T >
operator-( const os_num_complex< T >& lhs, const T& rhs );





/**
 * Complex Numbers subtraction binary operator. Subtracts
 * input real value from input complex number. The
 * imaginary value is not manipulated.
 * @param lhs input real value
 * @param rhs input complex number
 * @return complex subtraction of the lhs and rhs
 */

template< class T >
os_num_complex< T >
operator-( const T& lhs, const os_num_complex< T >& rhs );





/**
 * Complex Numbers multiplication binary operator.
 * Multiplies input complex number with input complex number.
 * @param lhs input complex number
 * @param rhs input complex number
 * @return complex product of the lhs and rhs
 */

template< class T >
os_num_complex< T >
operator*( const os_num_complex< T >& lhs, const os_num_complex< T >& rhs );





/**
 * Complex Numbers multiplication binary operator.
 * Multiplies input complex number with input real value.
 * The imaginary value is not manipulated.
 * @param lhs input real value
 * @param rhs input complex number
 * @return complex product of the lhs and rhs
 */

template< class T >
os_num_complex< T >
operator*( const os_num_complex< T >& lhs, const T& rhs );





/**
 * Complex Numbers multiplication binary operator.
 * Multiplies input real value with input complex number.
 * The imaginary value is not manipulated.
 * @param lhs input real value
 * @param rhs input complex number
 * @return complex product of the lhs and rhs
 */

template< class T >
os_num_complex< T >
operator*( const T& lhs, const os_num_complex< T >& rhs );





/**
 * Complex Numbers division binary operator. Divides input
 * complex number with input complex number.
 * @param lhs input complex number
 * @param rhs input complex number
 * @return complex result of the division
 */

template< class T >
os_num_complex< T >
operator/( const os_num_complex< T >& lhs, const os_num_complex< T >& rhs );





/**
 * Complex Numbers division binary operator. Divides input
 * complex number with real value. The imaginary value is
 * not manipulated.
 * @param lhs input complex number
 * @param rhs input real value
 * @return complex result of the division
 */

template< class T >
os_num_complex< T >
operator/( const os_num_complex< T >& lhs, const T& rhs );





/**
 * Complex Numbers division binary operator. Divides input
 * real value with complex number. The imaginary value is
 * not manipulated.
 * @param lhs input real value
 * @param rhs input complex number
 * @return complex result of the division
 */

template< class T >
os_num_complex< T >
operator/( const T& lhs, const os_num_complex< T >& rhs );





/**
 * Complex Numbers equality operator. Compares the value
 * of input complex number with real value. The imaginary
 * value is not manipulated.
 * @param lhs input complex number
 * @param rhs input real value
 * @return true if equal, false otherwise
 */

template< class T >
bool
operator==( const os_num_complex< T >& lhs, const T& rhs );





/**
 * Complex Numbers equality operator. Compares the input
 * real value with input complex number. The imaginary
 * value is not manipulated.
 * @param lhs input real value
 * @param rhs input complex number
 * @return true if equal, false otherwise
 */

template< class T >
bool
operator==( const T& lhs, const os_num_complex< T >& rhs );





/**
 * Complex Numbers inequality operator. Reverse of
 * equality operator.
 * @param lhs input complex number
 * @param rhs input real value
 * @return true if equal, false otherwise
 */

template< class T >
bool
operator!=( const os_num_complex< T >& lhs, const T& rhs );





/**
 * Complex Numbers inequality operator. Reverse of
 * equality operator.
 * @param lhs input real value
 * @param rhs input complex number
 * @return true if equal, false otherwise
 */

template< class T >
bool
operator!=( const T& lhs, const os_num_complex< T >& rhs );





template< class T >
inline T
os_num_complex< T >::real() const
  {
  return m_real_;
  }





template< class T >
inline T
os_num_complex< T >::imag() const
  {
  return m_imaginary_;
  }





template< class T >
inline os_num_complex< T >&
os_num_complex< T >::operator+=( const os_num_complex& rhs )
  {
  m_real_ += rhs.real();
  m_imaginary_ += rhs.imag();
  return *this;
  }





template< class T >
inline os_num_complex< T >&
os_num_complex< T >::operator-=( const os_num_complex& rhs )
  {
  m_real_ -= rhs.real();
  m_imaginary_ -= rhs.imag();
  return *this;
  }





template< class T >
inline os_num_complex< T >&
os_num_complex< T >::operator*=( const os_num_complex& rhs )
  {
  //
  // (a + ib) * (c + id) Real Part = (ac - bd) Complex
  // Part = (a + b) * (c + d) - ac - bd
  //
  T l_a = rhs.real();
  T l_b = rhs.imag();
  T l_ac = l_a * m_real_;
  T l_bd = l_b * m_imaginary_;
  m_real_ = (l_ac -l_bd);
  m_imaginary_ = (l_a + l_b) * (m_real_ + m_imaginary_) -l_ac -l_bd;
  return *this;
  }





template< class T >
inline os_num_complex< T >&
os_num_complex< T >::operator/=( const os_num_complex& rhs )
  {
  //
  // (a + ib) / (c + id)(ac + bd)/(c^2 + d^2) + i((bc - ad) / (c^2 + d^2))
  //
  T l_a = m_real_;
  T l_b = m_imaginary_;
  T l_c = rhs.real();
  T l_d = rhs.imag();
  T l_csq_plus_dsq = (l_c) * (l_c) + (l_d) * (l_d);
  if ( l_csq_plus_dsq == 0 )
    {
    OS_THROW( os_math_toolkit_error, error_div_zero, "" )
    }

  T l_ac_plus_bd = (l_a * l_c) + (l_b * l_d);
  T l_bc_minus_ad = (l_b * l_c) -(l_a * l_d);
  m_real_ = l_ac_plus_bd / l_csq_plus_dsq;
  m_imaginary_ = l_bc_minus_ad / l_csq_plus_dsq;
  return *this;
  }





template< class T >
OS_STD_IO os_ostream&
operator<<( OS_STD_IO os_ostream& stream, const os_num_complex< T >& cmplx );


#ifdef OS_NO_AUTO_INSTANTIATE
#include <ospace/math/numcomplex.cc>
#endif


#endif // OS_MATH_NUMCOMPLEX_H