# Complex Numbers Revisited

suggest change

The code below implements a very simple complex number type for which the underlying field is automatically promoted, following the languageās type promotion rules, under application of the four basic operators (+, -, *, and /) with a member of a different field (be it another `complex<T>` or some scalar type).

This is intended to be a holistic example covering operator overloading alongside basic use of templates.

```#include <type_traits>

namespace not_std{

using std::decay_t;

//----------------------------------------------------------------
// complex< value_t >
//----------------------------------------------------------------

template<typename value_t>
struct complex
{
value_t x;
value_t y;

complex &operator += (const value_t &x)
{
this->x += x;
return *this;
}
complex &operator += (const complex &other)
{
this->x += other.x;
this->y += other.y;
return *this;
}

complex &operator -= (const value_t &x)
{
this->x -= x;
return *this;
}
complex &operator -= (const complex &other)
{
this->x -= other.x;
this->y -= other.y;
return *this;
}

complex &operator *= (const value_t &s)
{
this->x *= s;
this->y *= s;
return *this;
}
complex &operator *= (const complex &other)
{
(*this) = (*this) * other;
return *this;
}

complex &operator /= (const value_t &s)
{
this->x /= s;
this->y /= s;
return *this;
}
complex &operator /= (const complex &other)
{
(*this) = (*this) / other;
return *this;
}

complex(const value_t &x, const value_t &y)
: x{x}
, y{y}
{}

template<typename other_value_t>
explicit complex(const complex<other_value_t> &other)
: x{static_cast<const value_t &>(other.x)}
, y{static_cast<const value_t &>(other.y)}
{}

complex &operator = (const complex &) = default;
complex &operator = (complex &&) = default;
complex(const complex &) = default;
complex(complex &&) = default;
complex() = default;
};

// Absolute value squared
template<typename value_t>
value_t absqr(const complex<value_t> &z)
{ return z.x*z.x + z.y*z.y; }

//----------------------------------------------------------------
// operator - (negation)
//----------------------------------------------------------------

template<typename value_t>
complex<value_t> operator - (const complex<value_t> &z)
{ return {-z.x, -z.y}; }

//----------------------------------------------------------------
// operator +
//----------------------------------------------------------------

template<typename left_t,typename right_t>
auto operator + (const complex<left_t> &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a.x + b.x)>>
{ return{a.x + b.x, a.y + b.y}; }

template<typename left_t,typename right_t>
auto operator + (const left_t &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a + b.x)>>
{ return{a + b.x, b.y}; }

template<typename left_t,typename right_t>
auto operator + (const complex<left_t> &a, const right_t &b)
-> complex<decay_t<decltype(a.x + b)>>
{ return{a.x + b, a.y}; }

//----------------------------------------------------------------
// operator -
//----------------------------------------------------------------

template<typename left_t,typename right_t>
auto operator - (const complex<left_t> &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a.x - b.x)>>
{ return{a.x - b.x, a.y - b.y}; }

template<typename left_t,typename right_t>
auto operator - (const left_t &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a - b.x)>>
{ return{a - b.x, - b.y}; }

template<typename left_t,typename right_t>
auto operator - (const complex<left_t> &a, const right_t &b)
-> complex<decay_t<decltype(a.x - b)>>
{ return{a.x - b, a.y}; }

//----------------------------------------------------------------
// operator *
//----------------------------------------------------------------

template<typename left_t, typename right_t>
auto operator * (const complex<left_t> &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a.x * b.x)>>
{
return {
a.x*b.x - a.y*b.y,
a.x*b.y + a.y*b.x
};
}

template<typename left_t, typename right_t>
auto operator * (const left_t &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a * b.x)>>
{ return {a * b.x, a * b.y}; }

template<typename left_t, typename right_t>
auto operator * (const complex<left_t> &a, const right_t &b)
-> complex<decay_t<decltype(a.x * b)>>
{ return {a.x * b, a.y * b}; }

//----------------------------------------------------------------
// operator /
//----------------------------------------------------------------

template<typename left_t, typename right_t>
auto operator / (const complex<left_t> &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a.x / b.x)>>
{
const auto r = absqr(b);
return {
( a.x*b.x + a.y*b.y) / r,
(-a.x*b.y + a.y*b.x) / r
};
}

template<typename left_t, typename right_t>
auto operator / (const left_t &a, const complex<right_t> &b)
-> complex<decay_t<decltype(a / b.x)>>
{
const auto s = a/absqr(b);
return {
b.x * s,
-b.y * s
};
}

template<typename left_t, typename right_t>
auto operator / (const complex<left_t> &a, const right_t &b)
-> complex<decay_t<decltype(a.x / b)>>
{ return {a.x / b, a.y / b}; }

}// namespace not_std```
```int main(int argc, char **argv)
{
using namespace not_std;

complex<float> fz{4.0f, 1.0f};

// makes a complex<double>
auto dz = fz * 1.0;

// still a complex<double>
auto idz = 1.0f/dz;

// also a complex<double>
auto one = dz * idz;

// a complex<double> again
auto one_again = fz * idz;

// Operator tests, just to make sure everything compiles.

complex<float> a{1.0f, -2.0f};
complex<double> b{3.0, -4.0};

// All of these are complex<double>
auto c0 = a + b;
auto c1 = a - b;
auto c2 = a * b;
auto c3 = a / b;

// All of these are complex<float>
auto d0 = a + 1;
auto d1 = 1 + a;
auto d2 = a - 1;
auto d3 = 1 - a;
auto d4 = a * 1;
auto d5 = 1 * a;
auto d6 = a / 1;
auto d7 = 1 / a;

// All of these are complex<double>
auto e0 = b + 1;
auto e1 = 1 + b;
auto e2 = b - 1;
auto e3 = 1 - b;
auto e4 = b * 1;
auto e5 = 1 * b;
auto e6 = b / 1;
auto e7 = 1 / b;

return 0;
}```