#ifndef COMPLEXSAMPLET_HPP
#define COMPLEXSAMPLET_HPP

#include <iostream>

#include <boost/type_traits.hpp>
#include <boost/utility/enable_if.hpp>


template <class T> class complexsample {
	public:
	T re;
	T im;

	complexsample() {
	}

	template <class C> complexsample(const C& x, typename boost::enable_if< boost::is_convertible<C, T> >::type* =0) {
		re = x;
		im = 0.;
	}
	template <class C1, class C2> complexsample(const C1& x, const C2& y, typename boost::enable_if_c< boost::is_convertible<C1, T>::value && boost::is_convertible<C2, T>::value >::type* =0) {
		re = x;
		im = y;
	}

	friend complexsample operator+(const complexsample& x, const complexsample& y) {
		complexsample r;

		r.re = x.re + y.re;
		r.im = x.im + y.im;

		return r;
	}

	template <class C> friend typename boost::enable_if< boost::is_convertible<C, T>, complexsample >::type operator+(const complexsample& x, const C& y) {
		complexsample r;

		r.re = x.re + y;
		r.im = x.im;

		return r;
	}

	template <class C> friend typename boost::enable_if< boost::is_convertible<C, T>, complexsample >::type operator+(const C& x, const complexsample& y) {
		complexsample r;

		r.re = x + y.re;
		r.im = y.im;

		return r;
	}

	friend complexsample& operator+=(complexsample& x, const complexsample& y) {
		x = x + y;
		return x;
	}

	template <class C> friend typename boost::enable_if< boost::is_convertible<C, T>, complexsample& >::type operator+=(complexsample& x, const C& y) {
		x.re += y;
		return x;
	}

	friend complexsample operator-(const complexsample& x, const complexsample& y) {
		complexsample r;

		r.re = x.re - y.re;
		r.im = x.im - y.im;

		return r;
	}

	template <class C> friend typename boost::enable_if< boost::is_convertible<C, T>, complexsample >::type operator-(const complexsample& x, const C& y) {
		complexsample r;

		r.re = x.re - y;
		r.im = x.im;

		return r;
	}

	template <class C> friend typename boost::enable_if< boost::is_convertible<C, T>, complexsample >::type operator-(const C& x, const complexsample& y) {
		complexsample r;

		r.re = x - y.re;
		r.im = - y.im;

		return r;
	}

	friend complexsample& operator-=(complexsample& x, const complexsample& y) {
		x = x - y;
		return x;
	}

	template <class C> friend typename boost::enable_if< boost::is_convertible<C, T>, complexsample& >::type operator-=(complexsample& x, const C& y) {
		x.re -= y;
		return x;
	}

	friend complexsample operator-(const complexsample& x) {
		complexsample r;

		r.re = - x.re;
		r.im = - x.im;

		return r;
	}

	friend complexsample operator*(const complexsample& x, const complexsample& y) {
		complexsample r;

		r.re = x.re * y.re - x.im * y.im;
		r.im = x.re * y.im + x.im * y.re;

		return r;
	}

	template <class C> friend typename boost::enable_if< boost::is_convertible<C, T>, complexsample >::type operator*(const complexsample& x, const C& y) {
		complexsample r;

		r.re = x.re * y;
		r.im = x.im * y;

		return r;
	}

	template <class C> friend typename boost::enable_if< boost::is_convertible<C, T>, complexsample >::type operator*(const C& x, const complexsample& y) {
		complexsample r;

		r.re = x * y.re;
		r.im = x * y.im;

		return r;
	}

	friend complexsample& operator*=(complexsample& x, const complexsample& y) {
		x = x * y;
		return x;
	}

	template <class C> friend typename boost::enable_if< boost::is_convertible<C, T>, complexsample& >::type operator*=(complexsample& x, const C& y) {
		x.re *= y;
		x.im *= y;
		return x;
	}

	friend complexsample sqr(const complexsample& x) {
		complexsample r;

		r.re = x.re * x.re - x.im * x.im;
		r.im = 2. * x.re * x.im;

		return r;
	}

	friend std::ostream& operator<<(std::ostream& s, const complexsample& x) {
		s << '(' << x.re << '+' << x.im << "i)";
		return s;
	}
};

#endif // COMPLEXSAMPLET_HPP
