/* Tuplex.cpp
 *
 * Definition of the Tuplex class.
 */

#include "Tuplex.h"
#include <cmath>
#include <assert.h>

using namespace std;

Tuplex::Tuplex()
: r1_(),
  r2_(),
  r3_()
{
	// Nothing (more) to do
}

Tuplex::Tuplex(const Tuple r1, const Tuple r2, const Tuple r3)
: r1_(r1),
  r2_(r2),
  r3_(r3)
{
	// Nothing (more) to do
}

Tuplex::Tuplex(const Tuplex& toCopy)
: r1_(toCopy.r1()),
  r2_(toCopy.r2()),
  r3_(toCopy.r3())
{
	// Nothing (more) to do
}

Tuplex::~Tuplex()
{
	// Virtual stub; nothing to do
}

/* = : assignment
 *
 * Sets all rows to match.
 */
Tuplex& Tuplex::operator = (const Tuplex& toCopy)
{
	if (this != &toCopy)
	{
		r1_ = toCopy.r1();
		r2_ = toCopy.r2();
		r3_ = toCopy.r3();
	}
	return *this;
}

/* == : comparison
 *
 * True if all rows match.
 */
const bool Tuplex::operator == (const Tuplex& toCompare) const
{
	return (
		r1_ == toCompare.r1()
		&& r2_ == toCompare.r2()
		&& r3_ == toCompare.r3());
}

/* != : anti-comparison
 *
 * True if rows don't match.
 */
const bool Tuplex::operator != (const Tuplex& toCompare) const
{
	return !(*this == toCompare);
}

/* + : addition
 *
 * Element-wise sum of the two Tuplices.
 */
const Tuplex Tuplex::operator + (const Tuplex& toAdd) const
{
	Tuplex sum = *this;
	sum += toAdd;
	return sum;
}

/* += : unary addition
 *
 * Unary element-wise sum of the two Tuplices.
 */
Tuplex& Tuplex::operator += (const Tuplex& toAdd)
{
	r1_ += toAdd.r1();
	r2_ += toAdd.r2();
	r3_ += toAdd.r3();
	return *this;
}

/* - : negation
 *
 * Equivalent to multiplying by -1.
 */
const Tuplex Tuplex::operator - () const
{
	Tuplex neg = *this;
	neg *= -1;
	return neg;
}

/* - : subtraction
 *
 * Element-wise difference of the two Tuplices.
 */
const Tuplex Tuplex::operator - (const Tuplex& toSub) const
{
	Tuplex diff = *this;
	diff -= toSub;
	return diff;
}

/* -= : unary subtraction
 *
 * Unary element-wise difference of the two Tuplices.
 */
Tuplex& Tuplex::operator -= (const Tuplex& toSub)
{
	r1_ -= toSub.r1();
	r2_ -= toSub.r2();
	r3_ -= toSub.r3();
	return *this;
}

/* * : matrix multiplication
 *
 * Product of the two Tuplices.  Unary function is not provided because matrix
 * multiplication is non-abelian and it is unclear which side multiplication
 * should act upon.
 */
const Tuplex Tuplex::operator * (const Tuplex& toMultiply) const
{
	return Tuplex(
		r1_ * toMultiply,
		r2_ * toMultiply,
		r3_ * toMultiply);
}

/* *= : unary scalar multiplication
 *
 * Unary element-wise product of the Tuplex and scalar.
 */
Tuplex& Tuplex::operator *= (double scaleFactor)
{
	r1_ *= scaleFactor;
	r2_ *= scaleFactor;
	r3_ *= scaleFactor;
	return *this;
}

/* /= : unary scalar division
 *
 * Unary element-wise quotient of the Tuplex and scalar.
 */
Tuplex& Tuplex::operator /= (double scaleFactor)
{
	assert(scaleFactor != 0);

	r1_ /= scaleFactor;
	r2_ /= scaleFactor;
	r3_ /= scaleFactor;
	return *this;
}

/* * : right vector multiplication
 *
 * Multiply Tuplex by a Tuple on the right.
 */
const Tuple operator * (const Tuplex& transform, const Tuple& toMultiply)
{
	return Tuple(
		transform.r1().dot(toMultiply),
		transform.r2().dot(toMultiply),
		transform.r3().dot(toMultiply));
}

/* * : left vector multiplication
 *
 * Multiply Tuplex by a Tuple on the left.
 */
const Tuple operator * (const Tuple& toMultiply, const Tuplex& transform)
{
	return Tuple(
		toMultiply.dot(transform.c1()),
		toMultiply.dot(transform.c2()),
		toMultiply.dot(transform.c3()));
}

/* * : scalar multiplication
 *
 * Multiply each element of the given Tuplex by the given scalar.
 */
const Tuplex operator * (const Tuplex& toMultiply, const double& scaleFactor)
{
	Tuplex product = toMultiply;
	product *= scaleFactor;
	return product;
}

/* * : scalar multiplication, reverse order
 *
 * Multiply each element of the given Tuplex by the given scalar.
 */
const Tuplex operator * (const double& scaleFactor, const Tuplex& toMultiply)
{
	return toMultiply * scaleFactor;
}

/* / : scalar division
 *
 * Divide each element of the given Tuplex by the given scalar.
 */
const Tuplex operator / (const Tuplex& toDivide, const double& scaleFactor)
{
	Tuplex quotient = toDivide;
	quotient /= scaleFactor;
	return quotient;
}

/* << : write
 *
 * Output the given Tuple to a ostream.
 */
ostream& operator << (ostream& out_file, const Tuplex& theTuplex)
{
	out_file << "("
		<< theTuplex.r1() << ", "
		<< theTuplex.r2() << ", "
		<< theTuplex.r3() << ")";
	return out_file;
}
