/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.math.linear;

import org.apache.commons.math.fraction.Fraction;
import org.apache.commons.math.fraction.FractionConversionException;
import org.apache.commons.math.fraction.FractionField;

import junit.framework.TestCase;

/**
 * Test cases for the {@link SparseFieldVector} class.
 *
 * @version $Revision: 728186 $ $Date: 2009-04-20 11:42:11 -0700 (Mon, 20 Apr
 *          2009) $
 */
public class SparseFieldVectorTest extends TestCase {

	//
	protected Fraction[][] ma1 = { { new Fraction(1), new Fraction(2), new Fraction(3) },
			{ new Fraction(4), new Fraction(5), new Fraction(6) },
			{ new Fraction(7), new Fraction(8), new Fraction(9) } };
	protected Fraction[] vec1 = { new Fraction(1), new Fraction(2), new Fraction(3) };
	protected Fraction[] vec2 = { new Fraction(4), new Fraction(5), new Fraction(6) };
	protected Fraction[] vec3 = { new Fraction(7), new Fraction(8), new Fraction(9) };
	protected Fraction[] vec4 = { new Fraction(1), new Fraction(2), new Fraction(3), new Fraction(4), new Fraction(5),
			new Fraction(6), new Fraction(7), new Fraction(8), new Fraction(9) };
	protected Fraction[] vec_null = { new Fraction(0), new Fraction(0), new Fraction(0) };
	protected Fraction[] dvec1 = { new Fraction(1), new Fraction(2), new Fraction(3), new Fraction(4), new Fraction(5),
			new Fraction(6), new Fraction(7), new Fraction(8), new Fraction(9) };
	protected Fraction[][] mat1 = { { new Fraction(1), new Fraction(2), new Fraction(3) },
			{ new Fraction(4), new Fraction(5), new Fraction(6) },
			{ new Fraction(7), new Fraction(8), new Fraction(9) } };

	// tolerances
	protected double entryTolerance = 10E-16;
	protected double normTolerance = 10E-14;

	protected FractionField field = FractionField.getInstance();

	public void testMapFunctions() throws FractionConversionException {
		SparseFieldVector<Fraction> v1 = new SparseFieldVector<Fraction>(field, vec1);

		// octave = v1 .+ 2.0
		FieldVector<Fraction> v_mapAdd = v1.mapAdd(new Fraction(2));
		Fraction[] result_mapAdd = { new Fraction(3), new Fraction(4), new Fraction(5) };
		assertEquals("compare vectors", result_mapAdd, v_mapAdd.getData());

		// octave = v1 .+ 2.0
		FieldVector<Fraction> v_mapAddToSelf = v1.copy();
		v_mapAddToSelf.mapAddToSelf(new Fraction(2));
		Fraction[] result_mapAddToSelf = { new Fraction(3), new Fraction(4), new Fraction(5) };
		assertEquals("compare vectors", result_mapAddToSelf, v_mapAddToSelf.getData());

		// octave = v1 .- 2.0
		FieldVector<Fraction> v_mapSubtract = v1.mapSubtract(new Fraction(2));
		Fraction[] result_mapSubtract = { new Fraction(-1), new Fraction(0), new Fraction(1) };
		assertEquals("compare vectors", result_mapSubtract, v_mapSubtract.getData());

		// octave = v1 .- 2.0
		FieldVector<Fraction> v_mapSubtractToSelf = v1.copy();
		v_mapSubtractToSelf.mapSubtractToSelf(new Fraction(2));
		Fraction[] result_mapSubtractToSelf = { new Fraction(-1), new Fraction(0), new Fraction(1) };
		assertEquals("compare vectors", result_mapSubtractToSelf, v_mapSubtractToSelf.getData());

		// octave = v1 .* 2.0
		FieldVector<Fraction> v_mapMultiply = v1.mapMultiply(new Fraction(2));
		Fraction[] result_mapMultiply = { new Fraction(2), new Fraction(4), new Fraction(6) };
		assertEquals("compare vectors", result_mapMultiply, v_mapMultiply.getData());

		// octave = v1 .* 2.0
		FieldVector<Fraction> v_mapMultiplyToSelf = v1.copy();
		v_mapMultiplyToSelf.mapMultiplyToSelf(new Fraction(2));
		Fraction[] result_mapMultiplyToSelf = { new Fraction(2), new Fraction(4), new Fraction(6) };
		assertEquals("compare vectors", result_mapMultiplyToSelf, v_mapMultiplyToSelf.getData());

		// octave = v1 ./ 2.0
		FieldVector<Fraction> v_mapDivide = v1.mapDivide(new Fraction(2));
		Fraction[] result_mapDivide = { new Fraction(.5d), new Fraction(1), new Fraction(1.5d) };
		assertEquals("compare vectors", result_mapDivide, v_mapDivide.getData());

		// octave = v1 ./ 2.0
		FieldVector<Fraction> v_mapDivideToSelf = v1.copy();
		v_mapDivideToSelf.mapDivideToSelf(new Fraction(2));
		Fraction[] result_mapDivideToSelf = { new Fraction(.5d), new Fraction(1), new Fraction(1.5d) };
		assertEquals("compare vectors", result_mapDivideToSelf, v_mapDivideToSelf.getData());

		// octave = v1 .^-1
		FieldVector<Fraction> v_mapInv = v1.mapInv();
		Fraction[] result_mapInv = { new Fraction(1), new Fraction(0.5d), new Fraction(3.333333333333333e-01d) };
		assertEquals("compare vectors", result_mapInv, v_mapInv.getData());

		// octave = v1 .^-1
		FieldVector<Fraction> v_mapInvToSelf = v1.copy();
		v_mapInvToSelf.mapInvToSelf();
		Fraction[] result_mapInvToSelf = { new Fraction(1), new Fraction(0.5d), new Fraction(3.333333333333333e-01d) };
		assertEquals("compare vectors", result_mapInvToSelf, v_mapInvToSelf.getData());

	}

	public void testBasicFunctions() throws FractionConversionException {
		SparseFieldVector<Fraction> v1 = new SparseFieldVector<Fraction>(field, vec1);
		SparseFieldVector<Fraction> v2 = new SparseFieldVector<Fraction>(field, vec2);

		SparseFieldVector<Fraction> v2_t = new SparseFieldVector<Fraction>(field, vec2);

		// octave = v1 + v2
		FieldVector<Fraction> v_add = v1.add(v2);
		Fraction[] result_add = { new Fraction(5), new Fraction(7), new Fraction(9) };
		assertEquals("compare vect", v_add.getData(), result_add);

		SparseFieldVector<Fraction> vt2 = new SparseFieldVector<Fraction>(field, vec2);
		FieldVector<Fraction> v_add_i = v1.add(vt2);
		Fraction[] result_add_i = { new Fraction(5), new Fraction(7), new Fraction(9) };
		assertEquals("compare vect", v_add_i.getData(), result_add_i);

		// octave = v1 - v2
		SparseFieldVector<Fraction> v_subtract = v1.subtract(v2);
		Fraction[] result_subtract = { new Fraction(-3), new Fraction(-3), new Fraction(-3) };
		assertClose("compare vect", v_subtract.getData(), result_subtract, normTolerance);

		FieldVector<Fraction> v_subtract_i = v1.subtract(vt2);
		Fraction[] result_subtract_i = { new Fraction(-3), new Fraction(-3), new Fraction(-3) };
		assertClose("compare vect", v_subtract_i.getData(), result_subtract_i, normTolerance);

		// octave v1 .* v2
		FieldVector<Fraction> v_ebeMultiply = v1.ebeMultiply(v2);
		Fraction[] result_ebeMultiply = { new Fraction(4), new Fraction(10), new Fraction(18) };
		assertClose("compare vect", v_ebeMultiply.getData(), result_ebeMultiply, normTolerance);

		FieldVector<Fraction> v_ebeMultiply_2 = v1.ebeMultiply(v2_t);
		Fraction[] result_ebeMultiply_2 = { new Fraction(4), new Fraction(10), new Fraction(18) };
		assertClose("compare vect", v_ebeMultiply_2.getData(), result_ebeMultiply_2, normTolerance);

		// octave v1 ./ v2
		FieldVector<Fraction> v_ebeDivide = v1.ebeDivide(v2);
		Fraction[] result_ebeDivide = { new Fraction(0.25d), new Fraction(0.4d), new Fraction(0.5d) };
		assertClose("compare vect", v_ebeDivide.getData(), result_ebeDivide, normTolerance);

		FieldVector<Fraction> v_ebeDivide_2 = v1.ebeDivide(v2_t);
		Fraction[] result_ebeDivide_2 = { new Fraction(0.25d), new Fraction(0.4d), new Fraction(0.5d) };
		assertClose("compare vect", v_ebeDivide_2.getData(), result_ebeDivide_2, normTolerance);

		// octave dot(v1,v2)
		Fraction dot = v1.dotProduct(v2);
		assertEquals("compare val ", new Fraction(32), dot);

		// octave dot(v1,v2_t)
		Fraction dot_2 = v1.dotProduct(v2_t);
		assertEquals("compare val ", new Fraction(32), dot_2);

		FieldMatrix<Fraction> m_outerProduct = v1.outerProduct(v2);
		assertEquals("compare val ", new Fraction(4), m_outerProduct.getEntry(0, 0));

		FieldMatrix<Fraction> m_outerProduct_2 = v1.outerProduct(v2_t);
		assertEquals("compare val ", new Fraction(4), m_outerProduct_2.getEntry(0, 0));

	}

	public void testMisc() {
		SparseFieldVector<Fraction> v1 = new SparseFieldVector<Fraction>(field, vec1);

		String out1 = v1.toString();
		assertTrue("some output ", out1.length() != 0);
		try {
			v1.checkVectorDimensions(2);
			fail("IllegalArgumentException expected");
		} catch (IllegalArgumentException ex) {
			// expected behavior
		} catch (Exception e) {
			fail("wrong exception caught");
		}

	}

	public void testPredicates() {

		SparseFieldVector<Fraction> v = new SparseFieldVector<Fraction>(field,
				new Fraction[] { new Fraction(0), new Fraction(1), new Fraction(2) });

		v.setEntry(0, field.getZero());
		assertEquals(v, new SparseFieldVector<Fraction>(field,
				new Fraction[] { new Fraction(0), new Fraction(1), new Fraction(2) }));
		assertNotSame(v, new SparseFieldVector<Fraction>(field,
				new Fraction[] { new Fraction(0), new Fraction(1), new Fraction(2), new Fraction(3) }));

	}

	/** verifies that two vectors are close (sup norm) */
	protected void assertEquals(String msg, Fraction[] m, Fraction[] n) {
		if (m.length != n.length) {
			fail("vectors have different lengths");
		}
		for (int i = 0; i < m.length; i++) {
			assertEquals(msg + " " + i + " elements differ", m[i], n[i]);
		}
	}

	/** verifies that two vectors are close (sup norm) */
	protected void assertClose(String msg, Fraction[] m, Fraction[] n, double tolerance) {
		if (m.length != n.length) {
			fail("vectors have different lengths");
		}
		for (int i = 0; i < m.length; i++) {
			assertEquals(msg + " " + i + " elements differ", m[i].doubleValue(), n[i].doubleValue(), tolerance);
		}
	}

}
