#ifndef WIDGETS_VECTOR2D_H
#define WIDGETS_VECTOR2D_H

#include <QPointF>
#include <QRectF>
#include <QMetaType>

#include <tr1/memory>
#include <vector>
#include <cmath>

#include <stdint.h>
#include <sys/types.h>

#include <boost/optional.hpp>

#if defined(WIDGETS_LIBRARY)
#  define WIDGETS_EXPORT Q_DECL_EXPORT
#else
#  define WIDGETS_EXPORT Q_DECL_IMPORT
#endif


// -- TDE NAMESPACE ----------------------------------------------------

namespace TDE {

// -- Widgets NAMESPACE ------------------------------------------------

namespace Widgets {

namespace Plot {


class Vector2D;
class Vector3D;
class Matrix3D;
class Point;
class Range;
class Range2D;


// =====================================================================
//    Vector2D
// ---------------------------------------------------------------------

class Vector2D
{
public:
	Vector2D()
	{
		m_v[0] = 0.0; m_v[1] = 0.0;
	}
	Vector2D(double x0, double x1)
	{
		m_v[0] = x0;
		m_v[1] = x1;
	}
	Vector2D(double values[])
	{
		m_v[0] = values[0];
		m_v[1] = values[1];
	}

	Vector2D(const Vector2D& other)
	{
		for (int i = 0; i < 2; i++)
			m_v[i] = other.m_v[i];
	}

	virtual ~Vector2D() {}

	Vector3D homogeneous();

	Vector2D& operator= (const Vector2D& rhs)
	{
		for (int i = 0; i < 2; i++)
			m_v[i] = rhs.m_v[i];
		return *this;
	}
	bool operator== (const Vector2D& rhs) const
	{
		return (m_v[0] == rhs.m_v[0]) && (m_v[1] == rhs.m_v[1]);
	}
	bool operator!= (const Vector2D& rhs) const
	{
		return (! (*this == rhs));
	}
	double& operator[] (int index)
	{
		return m_v[index];
	}
	const double& operator[] (int index) const
	{
		return m_v[index];
	}

	double dot(const Vector2D& rhs)
	{
		return (m_v[0] * rhs[0]) + (m_v[1] * rhs[1]);
	}

	Vector2D& operator+= (const Vector2D& rhs)
	{
		for (int i = 0; i < 2; i++)
			m_v[i] += rhs.m_v[i];
		return *this;
	}
	Vector2D& operator+= (double rhs)
	{
		for (int i = 0; i < 2; i++)
			m_v[i] += rhs;
		return *this;
	}
	Vector2D& operator-= (const Vector2D& rhs)
	{
		for (int i = 0; i < 2; i++)
			m_v[i] -= rhs.m_v[i];
		return *this;
	}
	Vector2D& operator-= (double rhs)
	{
		for (int i = 0; i < 2; i++)
			m_v[i] -= rhs;
		return *this;
	}
	Vector2D& operator*= (const Vector2D& rhs)
	{
		for (int i = 0; i < 2; i++)
			m_v[i] *= rhs.m_v[i];
		return *this;
	}
	Vector2D& operator*= (double rhs)
	{
		for (int i = 0; i < 2; i++)
			m_v[i] *= rhs;
		return *this;
	}
	Vector2D& operator/= (const Vector2D& rhs)
	{
		for (int i = 0; i < 2; i++)
			m_v[i] /= rhs.m_v[i];
		return *this;
	}
	Vector2D& operator/= (double rhs)
	{
		for (int i = 0; i < 2; i++)
			m_v[i] /= rhs;
		return *this;
	}

	bool contains(double value)			{ return (value >= m_v[0]) && (value <= m_v[1]); }

	int rank() const					{ return 2; }

	double lengthSq() const				{ return (m_v[0] * m_v[0]) + (m_v[1] * m_v[1]); }
	double length() const				{ return std::sqrt(lengthSq()); }
	double length1d() const				{ return (m_v[1] - m_v[0]); }

	Vector2D& normalize()				{ double l = length(); return (*this /= l); }

	Vector2D abs() const				{ return Vector2D(std::abs(m_v[0]), std::abs(m_v[1])); }

	Vector2D lerp(const Vector2D& other, double value) const
	{
		return Vector2D(
			m_v[0] + (other.m_v[0] - m_v[0]) * value,
			m_v[1] + (other.m_v[1] - m_v[1]) * value
		);
	}

	virtual std::string repr() const;

	friend std::ostream& operator<< (std::ostream& out, const Vector2D& value);

protected:
	double m_v[2];
};

inline double dot(const Vector2D& v1, const Vector2D& v2)
{
	return (v1[0] * v2[0]) + (v1[1] * v2[1]);
}

inline Vector2D operator+ (const Vector2D& v1, const Vector2D& v2)
{
	return Vector2D(v1[0] + v2[0], v1[1] + v2[1]);
}

inline Vector2D operator+ (const Vector2D& v1, double v2)
{
	return Vector2D(v1[0] + v2, v1[1] + v2);
}

inline Vector2D operator- (const Vector2D& v1, const Vector2D& v2)
{
	return Vector2D(v1[0] - v2[0], v1[1] - v2[1]);
}

inline Vector2D operator- (const Vector2D& v1, double v2)
{
	return Vector2D(v1[0] - v2, v1[1] - v2);
}

inline Vector2D operator* (const Vector2D& v1, const Vector2D& v2)
{
	return Vector2D(v1[0] * v2[0], v1[1] * v2[1]);
}

inline Vector2D operator* (const Vector2D& v1, double v2)
{
	return Vector2D(v1[0] * v2, v1[1] * v2);
}

inline Vector2D operator/ (const Vector2D& v1, const Vector2D& v2)
{
	return Vector2D(v1[0] / v2[0], v1[1] / v2[1]);
}

inline Vector2D operator/ (const Vector2D& v1, double v2)
{
	return Vector2D(v1[0] / v2, v1[1] / v2);
}


// =====================================================================
//    Vector3D
// ---------------------------------------------------------------------

class Vector3D
{
public:
	enum { RANK = 3 };

	Vector3D()
	{
		m_v[0] = 0.0; m_v[1] = 0.0; m_v[2] = 0.0;
	}
	Vector3D(double x0, double x1, double x2)
	{
		m_v[0] = x0;
		m_v[1] = x1;
		m_v[2] = x2;
	}
	Vector3D(double values[])
	{
		m_v[0] = values[0];
		m_v[1] = values[1];
		m_v[2] = values[2];
	}

	Vector3D(const Vector3D& other)
	{
		for (int i = 0; i < RANK; i++)
			m_v[i] = other.m_v[i];
	}

	virtual ~Vector3D() {}

	static Vector3D Homogeneous(const Vector2D& vec2d)
	{
		return Vector3D(vec2d[0], vec2d[1], 1.0);
	}

	Vector3D& operator= (const Vector3D& rhs)
	{
		for (int i = 0; i < RANK; i++)
			m_v[i] = rhs.m_v[i];
		return *this;
	}
	bool operator== (const Vector3D& rhs) const
	{
		return (m_v[0] == rhs.m_v[0]) && (m_v[1] == rhs.m_v[1]) && (m_v[2] == rhs.m_v[2]);
	}
	bool operator!= (const Vector3D& rhs) const
	{
		return (! (*this == rhs));
	}
	double& operator[] (int index)
	{
		return m_v[index];
	}
	const double& operator[] (int index) const
	{
		return m_v[index];
	}

	double dot(const Vector3D& rhs)
	{
		return (m_v[0] * rhs[0]) + (m_v[1] * rhs[1]) + (m_v[2] * rhs[2]);
	}

	Vector3D& operator+= (const Vector3D& rhs)
	{
		for (int i = 0; i < RANK; i++)
			m_v[i] += rhs.m_v[i];
		return *this;
	}
	Vector3D& operator+= (double rhs)
	{
		for (int i = 0; i < RANK; i++)
			m_v[i] += rhs;
		return *this;
	}
	Vector3D& operator-= (const Vector3D& rhs)
	{
		for (int i = 0; i < RANK; i++)
			m_v[i] -= rhs.m_v[i];
		return *this;
	}
	Vector3D& operator-= (double rhs)
	{
		for (int i = 0; i < RANK; i++)
			m_v[i] -= rhs;
		return *this;
	}
	Vector3D& operator*= (const Vector3D& rhs)
	{
		for (int i = 0; i < RANK; i++)
			m_v[i] *= rhs.m_v[i];
		return *this;
	}
	Vector3D& operator*= (double rhs)
	{
		for (int i = 0; i < RANK; i++)
			m_v[i] *= rhs;
		return *this;
	}
	Vector3D& operator/= (const Vector3D& rhs)
	{
		for (int i = 0; i < RANK; i++)
			m_v[i] /= rhs.m_v[i];
		return *this;
	}
	Vector3D& operator/= (double rhs)
	{
		for (int i = 0; i < RANK; i++)
			m_v[i] /= rhs;
		return *this;
	}

	int rank() const					{ return 3; }

	double lengthSq() const				{ return (m_v[0] * m_v[0]) + (m_v[1] * m_v[1]) + (m_v[2] * m_v[2]); }
	double length() const				{ return std::sqrt(lengthSq()); }

	Vector3D& normalize()				{ double l = length(); return (*this /= l); }

	Vector3D& dehomogenize()
	{
		m_v[0] /= m_v[2];
		m_v[1] /= m_v[2];
		m_v[2] /= m_v[2];
		return *this;
	}

	Vector2D dehomogenize2d() const
	{
		return Vector2D(m_v[0] / m_v[2], m_v[1] / m_v[2]);
	}

	Vector3D abs() const				{ return Vector3D(std::abs(m_v[0]), std::abs(m_v[1]), std::abs(m_v[2])); }

	Vector3D lerp(const Vector3D& other, double value) const
	{
		return Vector3D(
			m_v[0] + (other.m_v[0] - m_v[0]) * value,
			m_v[1] + (other.m_v[1] - m_v[1]) * value,
			m_v[2] + (other.m_v[2] - m_v[2]) * value
		);
	}

	friend Vector3D operator* (const Matrix3D& lhs, const Vector3D& rhs);

	virtual std::string repr() const;

	friend std::ostream& operator<< (std::ostream& out, const Vector3D& value);

protected:
	double m_v[RANK];
};

inline double dot(const Vector3D& v1, const Vector3D& v2)
{
	return (v1[0] * v2[0]) + (v1[1] * v2[1]) + (v1[2] * v2[2]);
}

inline Vector3D operator+ (const Vector3D& v1, const Vector3D& v2)
{
	return Vector3D(v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2]);
}

inline Vector3D operator+ (const Vector3D& v1, double v2)
{
	return Vector3D(v1[0] + v2, v1[1] + v2, v1[2] + v2);
}

inline Vector3D operator- (const Vector3D& v1, const Vector3D& v2)
{
	return Vector3D(v1[0] - v2[0], v1[1] - v2[1], v1[2] - v2[2]);
}

inline Vector3D operator- (const Vector3D& v1, double v2)
{
	return Vector3D(v1[0] - v2, v1[1] - v2, v1[2] - v2);
}

inline Vector3D operator* (const Vector3D& v1, const Vector3D& v2)
{
	return Vector3D(v1[0] * v2[0], v1[1] * v2[1], v1[2] * v2[2]);
}

inline Vector3D operator* (const Vector3D& v1, double v2)
{
	return Vector3D(v1[0] * v2, v1[1] * v2, v1[2] * v2);
}

inline Vector3D operator/ (const Vector3D& v1, const Vector3D& v2)
{
	return Vector3D(v1[0] / v2[0], v1[1] / v2[1], v1[2] / v2[2]);
}

inline Vector3D operator/ (const Vector3D& v1, double v2)
{
	return Vector3D(v1[0] / v2, v1[1] / v2, v1[2] / v2);
}

inline Vector3D Vector2D::homogeneous()
{
	return Vector3D(m_v[0], m_v[1], 1.0);
}


// =====================================================================
//    Matrix3D
// ---------------------------------------------------------------------

class Matrix3D
{
public:
	enum { ROWS = 3 };
	enum { COLUMNS = 3 };

	Matrix3D() : m_invalid(false)
	{
		for (int i = 0; i < ROWS; i++)
			for (int j = 0; j < COLUMNS; j++)
				m_v[i][j] = 0.0;
	}

	Matrix3D(const double v[3][3]) : m_invalid(false)
	{
		for (int i = 0; i < ROWS; i++)
			for (int j = 0; j < COLUMNS; j++)
				m_v[i][j] = v[i][j];
	}

	Matrix3D(const Vector3D& row0, const Vector3D& row1, const Vector3D& row2) : m_invalid(false)
	{
		m_v[0] = row0;
		m_v[1] = row1;
		m_v[2] = row2;
	}

	Matrix3D(const Vector3D rows[3]) : m_invalid(false)
	{
		for (int i = 0; i < ROWS; i++)
			m_v[i] = rows[i];
	}

	Matrix3D(const Matrix3D& other) : m_invalid(false)
	{
		for (int i = 0; i < ROWS; i++)
			m_v[i] = other.m_v[i];
		m_invalid = other.m_invalid;
	}

	virtual ~Matrix3D() {}

	Matrix3D ByRows(const Vector3D rows[3])
	{
		Matrix3D r;
		for (int i = 0; i < ROWS; i++)
			r.m_v[i] = rows[i];
		return r;
	}

	Matrix3D ByColumns(const Vector3D columns[3])
	{
		Matrix3D r;
		for (int j = 0; j < COLUMNS; j++)
		{
			for (int i = 0; i < ROWS; i++)
				r.m_v[i][j] = columns[j][i];
		}
		return r;
	}

	static Matrix3D Invalid()
	{
		Matrix3D r;
		r.invalidate();
		return r;
	}

	static Matrix3D Zero()
	{
		return Matrix3D();
	}

	static Matrix3D One()
	{
		Matrix3D r;
		for (int i = 0; i < ROWS; i++)
			for (int j = 0; j < COLUMNS; j++)
				r.m_v[i][j] = (i == j) ? 1.0 : 0.0;
		return r;
	}

	static Matrix3D Scale(double sx, double sy)
	{
		Matrix3D r;
		r.m_v[0][0] = sx;
		r.m_v[1][1] = sy;
		r.m_v[2][2] = 1.0;
		return r;
	}

	static Matrix3D Translate(double dx, double dy)
	{
		Matrix3D r;
		r.m_v[0][0] = 1.0;
		r.m_v[1][1] = 1.0;
		r.m_v[2][2] = 1.0;

		r.m_v[0][2] = dx;
		r.m_v[1][2] = dy;
		return r;
	}

	// return the transformation matrix mapping from the `src` area
	// to the `dst` one
	static Matrix3D MapArea(const Range2D& src, const Range2D& dst, bool invert_y = false);

	void invalidate() { m_invalid = true; }
	bool isInvalid() const { return m_invalid; }
	bool isValid() const { return (! isInvalid()); }
	bool isZero() const { return *this == Zero(); }

	Matrix3D& zero()
	{
		m_invalid = false;
		for (int i = 0; i < ROWS; i++)
			for (int j = 0; j < COLUMNS; j++)
				m_v[i][j] = 0.0;
		return *this;
	}

	Matrix3D& one()
	{
		m_invalid = false;
		for (int i = 0; i < ROWS; i++)
			for (int j = 0; j < COLUMNS; j++)
				m_v[i][j] = (i == j) ? 1.0 : 0.0;
		return *this;
	}

	double determinant() const;

	Matrix3D inverse() const;

	Matrix3D& operator= (const Matrix3D& rhs)
	{
		for (int i = 0; i < ROWS; i++)
			m_v[i] = rhs.m_v[i];
		m_invalid = rhs.m_invalid;
		return *this;
	}
	bool operator== (const Matrix3D& rhs) const
	{
		bool eq = true;
		for (int i = 0; i < ROWS; i++)
			eq = eq && (m_v[i] == rhs.m_v[i]);
		return eq;
	}
	bool operator!= (const Matrix3D& rhs) const
	{
		return (! (*this == rhs));
	}
	Vector3D& operator[] (int index)
	{
		return m_v[index];
	}
	const Vector3D& operator[] (int index) const
	{
		return m_v[index];
	}

	Matrix3D& operator+= (const Matrix3D& rhs)
	{
		for (int i = 0; i < ROWS; i++)
			m_v[i] += rhs.m_v[i];
		return *this;
	}
	Matrix3D& operator+= (double rhs)
	{
		for (int i = 0; i < ROWS; i++)
			m_v[i] += rhs;
		return *this;
	}
	Matrix3D& operator-= (const Matrix3D& rhs)
	{
		for (int i = 0; i < ROWS; i++)
			m_v[i] -= rhs.m_v[i];
		return *this;
	}
	Matrix3D& operator-= (double rhs)
	{
		for (int i = 0; i < ROWS; i++)
			m_v[i] -= rhs;
		return *this;
	}
	Matrix3D& operator*= (const Matrix3D& rhs)
	{
		Matrix3D tmp = *this;
		*this = tmp * rhs;
		return *this;
	}
	Matrix3D& operator*= (double rhs)
	{
		for (int i = 0; i < ROWS; i++)
			m_v[i] *= rhs;
		return *this;
	}
	Matrix3D& operator/= (double rhs)
	{
		for (int i = 0; i < ROWS; i++)
			m_v[i] /= rhs;
		return *this;
	}

	friend Matrix3D operator* (const Matrix3D& lhs, const Matrix3D& rhs);
	friend Vector3D operator* (const Matrix3D& lhs, const Vector3D& rhs);

	virtual std::string repr() const;

	friend std::ostream& operator<< (std::ostream& out, const Matrix3D& value);

protected:
	Vector3D m_v[ROWS];
	bool m_invalid;
};


inline Matrix3D operator+ (const Matrix3D& m1, const Matrix3D& m2)
{
	return Matrix3D(m1[0] + m2[0], m1[1] + m2[1], m1[2] + m2[2]);
}

inline Matrix3D operator+ (const Matrix3D& m1, double v2)
{
	return Matrix3D(m1[0] + v2, m1[1] + v2, m1[2] + v2);
}

inline Matrix3D operator- (const Matrix3D& m1, const Matrix3D& m2)
{
	return Matrix3D(m1[0] - m2[0], m1[1] - m2[1], m1[2] - m2[2]);
}

inline Matrix3D operator- (const Matrix3D& m1, double v2)
{
	return Matrix3D(m1[0] - v2, m1[1] - v2, m1[2] - v2);
}

inline Matrix3D operator* (const Matrix3D& m1, const Matrix3D& m2)
{
	Matrix3D r;

	for (int i = 0; i < Matrix3D::ROWS; i++)
	{
		for (int j = 0; j < Matrix3D::COLUMNS; j++)
		{
			double v = 0.0;

			for (int k = 0; k < Matrix3D::COLUMNS; k++)
				v += m1.m_v[i][k] * m2.m_v[k][j];

			r.m_v[i][j] = v;
		}
	}

	return r;
}

inline Vector3D operator* (const Matrix3D& m1, const Vector3D& v2)
{
	Vector3D r;

	for (int i = 0; i < Matrix3D::ROWS; i++)
	{
		double v = 0.0;

		for (int k = 0; k < Matrix3D::COLUMNS; k++)
			v += m1.m_v[i][k] * v2.m_v[k];

		r.m_v[i] = v;
	}

	return r;
}

inline Matrix3D operator* (const Matrix3D& m1, double v2)
{
	return Matrix3D(m1[0] * v2, m1[1] * v2, m1[2] * v2);
}

inline Matrix3D operator/ (const Matrix3D& m1, double v2)
{
	return Matrix3D(m1[0] / v2, m1[1] / v2, m1[2] / v2);
}


// =====================================================================
//    Point
// ---------------------------------------------------------------------

class Point : public Vector3D
{
public:
	Point() : Vector3D() {}
	Point(double x, double y) : Vector3D(x, y, 1.0) {}
	Point(const Point& other) : Vector3D(other.ux(), other.uy(), other.uw()) {}
	Point(const Vector3D& vec) : Vector3D(vec[0], vec[1], vec[2]) {}
	Point(const Vector2D& vec) : Vector3D(vec[0], vec[1], 1.0) {}
	Point(const QPointF& qp) : Vector3D(qp.x(), qp.y(), 1.0) {}
	Point(const QPoint& qp) : Vector3D(qp.x(), qp.y(), 1.0) {}
	virtual ~Point() {}

	double x() const					{ return m_v[0] / m_v[2]; }
	double x(double value)				{ double old = m_v[0] * m_v[2]; m_v[0] = value * m_v[2]; return old; }
	double y() const					{ return m_v[1] / m_v[2]; }
	double y(double value)				{ double old = m_v[1] * m_v[2]; m_v[1] = value * m_v[2]; return old; }

	double ux() const					{ return m_v[0]; }
	double ux(double value)				{ double old = m_v[0]; m_v[0] = value; return old; }
	double uy() const					{ return m_v[1]; }
	double uy(double value)				{ double old = m_v[1]; m_v[1] = value; return old; }
	double uw() const					{ return m_v[2]; }
	double uw(double value)				{ double old = m_v[2]; m_v[2] = value; return old; }

	virtual std::string repr() const;
};

// =====================================================================
//    Range
// ---------------------------------------------------------------------

class Range
{
public:
	Range() : m_empty(true), m_r(1, -1) {}
	Range(double lo, double hi) : m_empty(false), m_r(lo, hi) {}
	Range(const Range& other) : m_empty(other.empty()), m_r(other.lo(), other.hi()) {}
	Range(const Vector2D& vec) : m_empty(false), m_r(vec[0], vec[1]) {}
	virtual ~Range() {}

	Range& operator= (const Range& rhs)
	{
		m_empty = rhs.m_empty;
		m_r = rhs.m_r;
		return *this;
	}
	bool operator== (const Range& rhs) const
	{
		return (m_empty == rhs.m_empty) && (m_r[0] == rhs.m_r[0]) && (m_r[1] == rhs.m_r[1]);
	}
	bool operator!= (const Range& rhs) const
	{
		return (! (*this == rhs));
	}
	const double& operator[] (int index) const
	{
		return m_r[index];
	}

	virtual bool empty() const			{ return m_empty; }
	virtual Range& clear()              { m_empty = true; m_r[0] = 1; m_r[1] = -1; return *this; }

	double lo() const					{ return m_r[0]; }
	double lo(double value)				{ double old = m_r[0]; m_r[0] = value; m_empty = false; return old; }
	double hi() const					{ return m_r[1]; }
	double hi(double value)				{ double old = m_r[1]; m_r[1] = value; m_empty = false; return old; }

	virtual bool extend(double value);
	virtual bool extend(const Vector2D& range);
	virtual bool extend(const Range& range);

	virtual bool extend(const std::vector<double>& values);

	virtual double width() const		{ return (m_r[1] - m_r[0]); }

	virtual bool contains(double value)	{ return (! empty()) && m_r.contains(value); }

	virtual Range Margin(double value) const;

	virtual std::string repr() const;

	friend std::ostream& operator<< (std::ostream& out, const Range& value);

protected:
	bool m_empty;
	Vector2D m_r;
};

// =====================================================================
//    Range2D
// ---------------------------------------------------------------------

class Range2D
{
public:
	Range2D() {}
	Range2D(Range x, Range y) : m_x(x), m_y(y) {}
	Range2D(const Range2D& other) : m_x(other.x()), m_y(other.y()) {}
	Range2D(const QRectF& qr) :
		m_x(qr.x(), qr.x() + qr.width()), m_y(qr.y(), qr.y() + qr.height()) {}
	virtual ~Range2D() {}

	Range2D& operator= (const Range2D& rhs)
	{
		m_x = rhs.m_x;
		m_y = rhs.m_y;
		return *this;
	}
	bool operator== (const Range2D& rhs) const
	{
		return (m_x == rhs.m_x) && (m_y == rhs.m_y);
	}
	bool operator!= (const Range2D& rhs) const
	{
		return (! (*this == rhs));
	}

	virtual bool empty() const			{ return m_x.empty() && m_y.empty(); }
	virtual Range2D& clear()            { m_x.clear(); m_y.clear(); return *this; }

	Range x() const						{ return m_x; }
	Range x(const Range& value)			{ Range old = m_x; m_x = value; return old; }
	Range y() const						{ return m_y; }
	Range y(const Range& value)			{ Range old = m_y; m_y = value; return old; }

	virtual bool extendX(double x);
	virtual bool extendX(const Range& range1d);
	virtual bool extendY(double y);
	virtual bool extendY(const Range& range1d);
	virtual bool extend(double x, double y);
	virtual bool extend(const Vector2D& vec);
	virtual bool extend(const Point& point);

	virtual bool extend(const Range2D& range2d);

	virtual bool extend(const std::vector<Point>& points);

	virtual double width() const		{ return m_x.width(); }
	virtual double height() const		{ return m_y.width(); }

	virtual bool contains(const Point& point)	{ return (! empty()) && m_x.contains(point.x()) && m_y.contains(point.y()); }

	virtual QRectF qRectF() const;

	virtual Range2D Margin(double value) const;

	virtual std::string repr() const;

	friend std::ostream& operator<< (std::ostream& out, const Range2D& value);

protected:
	Range m_x;
	Range m_y;
};


// =====================================================================
//    NewtonRaphson solver
// ---------------------------------------------------------------------

template <typename T,
		  double (T::*Value)(double) const,
		  double (T::*Derivative)(double) const>
class NewtonRaphson
{
public:
	NewtonRaphson(const T& Object) :
		m_tolerance(1.E-6), m_max_iterations(150), m_object(Object)
	{}

	void setTolerance(double tolerance)					{ m_tolerance = tolerance; }
	void setXRange(double x_min, double x_max) {
		m_xmin = boost::optional<double>(x_min);
		m_xmax = boost::optional<double>(x_max);
	}
	void setMaxIterations(size_t max_iterations)		{ m_max_iterations = max_iterations; }

	boost::optional<double> solve(double target, double x_start);

private:
	void _force_x_bounds(double& x, double xPrev) const;

	double m_tolerance;
	boost::optional<double> m_xmin, m_xmax;
	size_t m_max_iterations;
	const T& m_object;
};

template <typename T,
		  double (T::*Value)(double) const,
		  double (T::*Derivative)(double) const>
inline void NewtonRaphson<T, Value, Derivative>::_force_x_bounds(double& x, double xPrev) const
{
	if (m_xmin && m_xmax)
	{
		while ((x < (*m_xmin)) || (x > (*m_xmax)))
		{
			x = (x + xPrev) / 2.0;
		}
	} else if (m_xmin)
	{
		while (x < (*m_xmin))
		{
			x = (x + xPrev) / 2.0;
		}
	} else if (m_xmax)
	{
		while (x > (*m_xmax))
		{
			x = (x + xPrev) / 2.0;
		}
	}
}

template <class T,
		  double (T::*Value)(double) const,
		  double (T::*Derivative)(double) const>
inline boost::optional<double> NewtonRaphson<T, Value, Derivative>::solve(double target, double x_start)
{
	size_t count = 0;
	double xPrev = x_start;
	double x = x_start;
	double diff, derivative;

	if (m_xmin)
	{
		assert(x_start >= (*m_xmin));
	}

	if (m_xmax)
	{
		assert(x_start <= (*m_xmax));
	}

	while (count < m_max_iterations)
	{
		count++;

		_force_x_bounds(x, xPrev);

		double value = (m_object.*Value)(x);
		diff = target - value;
		if (fabs(diff) < m_tolerance)
		{
			//std::cout << "NewtonRaphson - FOUND root " << x << " for target " << target << " (value:" << value << " diff:" << diff << ")" << std::endl;
			return boost::optional<double>(x);
		}

		derivative = (m_object.*Derivative)(x);

		if (fabs(derivative) < 1.E-6)
		{
			if (x > xPrev)
			{
				if (m_xmax)
					x = (x + (*m_xmax)) / 2.0;
				else
					x += (x - xPrev);
				continue;
			} else
			{
				if (m_xmin)
					x = ((*m_xmin) + x) / 2.0;
				else
					x -= (xPrev - x);
				continue;
			}
		}

		xPrev = x;
		x = x - diff/(-derivative);
	}

	//std::cout << "NewtonRaphson - no root found for target " << target << std::endl;
	return boost::optional<double>();
}

void registerVectorTypesInQt();


} /* end of namespace Plot */

} /* end of namespace Widgets */

} /* end of namespace TDE */

Q_DECLARE_METATYPE(TDE::Widgets::Plot::Vector2D)
Q_DECLARE_METATYPE(TDE::Widgets::Plot::Point)
Q_DECLARE_METATYPE(TDE::Widgets::Plot::Range)
Q_DECLARE_METATYPE(TDE::Widgets::Plot::Range2D)

#endif /* WIDGETS_VECTOR2D_H */
