/*
* Copyright 2011-2013, by Vladimir Kostyukov and Contributors.
*
* This file is part of la4j project (http://la4j.org)
*
* Licensed 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.
*
* Contributor(s): Daniel Renshaw
*                 Jakob Moellers
*                 Yuriy Drozd
*                 Maxim Samoylov
*
*/

package org.la4j.vector;

import org.junit.Assert;
import org.junit.Test;
import org.la4j.V;
import org.la4j.Vector;
import org.la4j.Vectors;
import org.la4j.Matrix;
import org.la4j.vector.functor.VectorAccumulator;
import org.la4j.vector.functor.VectorPredicate;

import static org.la4j.V.*;
import static org.la4j.M.*;

import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public abstract class VectorTest<T extends Vector> {

    protected VectorFactory<T> factory;

    public VectorTest(VectorFactory<T> factory) {
        this.factory = factory;
    }

    public T v(double... values) {
        return V.v(values).to(factory);
    }

    public T vz(int length) {
        return V.vz(length).to(factory);
    }

    @Test
    public void testAccess_10() {
        Vector a = v(0.0, 0.0, 2.0, 0.0, 3.0, 0.0, 0.0, 0.0, 0.1, 2.0);

        Assert.assertEquals(10, a.length());
        Assert.assertEquals(0.0, a.get(0), Vectors.EPS);
        Assert.assertEquals(2.0, a.get(2), Vectors.EPS);
        Assert.assertEquals(2.0, a.get(9), Vectors.EPS);

        a.set(3, 42.0);
        a.set(8, 14.0);
        a.set(0, 3.3);

        Assert.assertEquals(42.0, a.get(3), Vectors.EPS);
        Assert.assertEquals(14.0, a.get(8), Vectors.EPS);
        Assert.assertEquals(3.3, a.get(0), Vectors.EPS);
    }

    @Test
    public void testSetAll_5() {
        Vector a = v(0.0, 0.0, 0.0, 0.0, 1.0, 4.0);
        a.setAll(11.1);

        for (double d: a) {
            Assert.assertEquals(11.1, d, Vectors.EPS);
        }
    }

    @Test
    public void testCopyOfLength_3_to_5_to_2() {
        Vector a = v(0.0, 0.0, 1.0);
        Vector b = v(0.0, 0.0, 1.0, 0.0, 0.0);
        Vector c = v(0.0, 0.0);

        a = a.copyOfLength(5);
        Assert.assertEquals(b, a);

        a = a.copyOfLength(2);
        Assert.assertEquals(c, a);
    }

    @Test
    public void testCopyOf_5_to_0_to_4() {
        Vector a = v(0.0, 1.0, 2.0, 3.0, 0.0);
        Vector b = v();
        Vector c = v(0.0, 0.0, 0.0, 0.0);

        a = a.copyOfLength(0);
        Assert.assertEquals(b, a);

        a = a.copyOfLength(4);
        Assert.assertEquals(c, a);
    }

    @Test
    public void testSlice_5_to_2_and_3() {
        Vector a = v(1.0, 2.0, 3.0, 4.0, 5.0);
        Vector b = v(2.0, 3.0);
        Vector c = v(3.0, 4.0, 5.0);

        Assert.assertEquals(b, a.slice(1, 3));
        Assert.assertEquals(c, a.slice(2, 5));
    }

    @Test
    public void testSliceLeftRight_5_to_1_and_4() {
        Vector a = v(0.0, 2.0, 0.0, 4.0, 0.0);
        Vector b = v(0.0);
        Vector c = v(2.0, 0.0, 4.0, 0.0);

        Assert.assertEquals(b, a.sliceLeft(1));
        Assert.assertEquals(c, a.sliceRight(1));
    }

    @Test
    public void testSelect_4() {
        Vector a = v(0.0, 3.0, 7.0, 0.0);
        Vector b = v(3.0, 0.0, 0.0, 7.0);
        Vector c = v(7.0, 7.0, 0.0, 0.0);

        Assert.assertEquals(b, a.select(new int[]{1, 0, 3, 2}));
        Assert.assertEquals(c, a.select(new int[]{2, 2, 0, 3}));
    }

    @Test
    public void testSelect_5() {
        Vector a = v(1.0, 6.0, 0.0, 0.0, 8.0);
        Vector b = v(1.0, 1.0, 1.0);
        Vector c = v(0.0, 0.0, 8.0, 8.0, 1.0, 0.0);

        Assert.assertEquals(b, a.select(new int[]{0, 0, 0}));
        Assert.assertEquals(c, a.select(new int[]{2, 3, 4, 4, 0, 3}));
    }

    @Test
    public void testSelect_3() {
        Vector a = v(1.0, 0.0, 0.0);
        Vector b = v(0.0, 0.0, 0.0, 0.0);
        Vector c = v(1.0);

        Assert.assertEquals(b, a.select(new int[]{1, 2, 2, 1}));
        Assert.assertEquals(c, a.select(new int[]{0}));
    }

    @Test
    public void testSwapElements_5() {
        Vector a = v(1.0, 0.0, 0.0, 0.0, 3.0);
        Vector b = v(3.0, 0.0, 0.0, 0.0, 1.0);

        a.swapElements(0, 4);
        Assert.assertEquals(b, a);
    }

    @Test
    public void testSwapElements_4() {
        Vector a = v(0.0, 1.0, 0.0, 0.0);
        Vector b = v(0.0, 0.0, 1.0, 0.0);

        a.swapElements(1, 2);
        Assert.assertEquals(b, a);
    }

    @Test
    public void testSwapElements_4_2() {
        Vector a = v(0.0, 1.0, 0.0, 2.0);
        Vector b = v(0.0, 0.0, 1.0, 2.0);

        a.swapElements(1, 2);
        Assert.assertEquals(a, b);
    }

    @Test
    public void testSwapElements_6() {
        Vector a = v(0.0, 0.0, 0.0, 0.0, 0.0, -5.0);
        Vector b = v(0.0, 0.0, 0.0, -5.0, 0.0, 0.0);

        a.swapElements(3, 5);
        Assert.assertEquals(a, b);
    }

    @Test
    public void testSwapElements_2() {
        Vector a = v(1.0, 2.0);
        Vector b = v(2.0, 1.0);

        a.swapElements(0, 1);
        Assert.assertEquals(b, a);
    }

    @Test
    public void testEuclideanNorm_3() {
        Vector a = v(1.0, 2.0, 3.0);
        Assert.assertEquals(3.74165, a.fold(Vectors.mkEuclideanNormAccumulator()), 1e-5);
        Assert.assertEquals(3.74165, a.euclideanNorm(), 1e-5);
    }

    @Test
    public void testEuclideanNorm_5() {
        Vector a = v(1.0, 0.0, 3.0, 0.0, -5.0);
        Assert.assertEquals(5.91607, a.fold(Vectors.mkEuclideanNormAccumulator()), 1e-5);
        Assert.assertEquals(5.91607, a.euclideanNorm(), 1e-5);
    }

    @Test
    public void testManhattanNorm_3() {
        Vector a = v(1.0, 2.0, 3.0);
        Assert.assertEquals(6.0, a.fold(Vectors.mkManhattanNormAccumulator()), 1e-5);
        Assert.assertEquals(6.0, a.manhattanNorm(), 1e-5);
    }

    @Test
    public void testManhattanNorm_5() {
        Vector a = v(1.0, 0.0, 3.0, 0.0, -5.0);
        Assert.assertEquals(9.0, a.fold(Vectors.mkManhattanNormAccumulator()), 1e-5);
        Assert.assertEquals(9.0, a.manhattanNorm(), 1e-5);
    }

    @Test
    public void testInfinityNorm_3() {
        Vector a = v(1.0, 2.0, 3.0);
        Assert.assertEquals(3.0, a.fold(Vectors.mkInfinityNormAccumulator()), 1e-5);
        Assert.assertEquals(3.0, a.infinityNorm(), 1e-5);
    }

    @Test
    public void testInfinityNorm_5() {
        Vector a = v(1.0, 0.0, 3.0, 0.0, -5.0);
        Assert.assertEquals(5.0, a.fold(Vectors.mkInfinityNormAccumulator()), 1e-5);
        Assert.assertEquals(5.0, a.infinityNorm(), 1e-5);
    }

    @Test
    public void testAdd_3() {
        for (Vector b: vs(0.0, 5.0, 0.0)) {
            Vector a = v(0.0, 0.0, 3.0);
            Vector c = v(7.0, 7.0, 10.0);
            Vector d = v(0.0, 5.0, 3.0);

            Assert.assertEquals(c, a.add(7.0));
            Assert.assertEquals(d, a.add(b));
            Assert.assertEquals(d, b.add(a));
        }
    }

    @Test
    public void testAdd_4() {
        for (Vector b: vs(0.0, 1.0, 0.0, 6.0)) {
            Vector a = v(1.0, 0.0, 0.0, 3.0);
            Vector c = v(1.0, 1.0, 0.0, 9.0);

            Assert.assertEquals(c, a.add(b));
            Assert.assertEquals(c, b.add(a));
        }
    }

    @Test
    public void testSubtract_3() {
        for (Vector b: vs(4.0, 0.0, 0.0)) {
            Vector a = v(0.0, 0.0, 3.0);
            Vector c = v(-7.0, -7.0, -4.0);
            Vector d = v(-4.0, 0.0, 3.0);

            Assert.assertEquals(c, a.subtract(7.0));
            Assert.assertEquals(d, a.subtract(b));
        }
    }

    @Test
    public void testMultiply_3() {
        for (Vector b: vs(0.0, 5.0, 0.0)) {
            Vector a = v(0.0, 0.0, 1.0);
            Vector c = v(0.0, 0.0, 10.0);
            Vector d = v(0.0, 0.0, 0.0);

            Assert.assertEquals(c, a.multiply(10.0));
            Assert.assertEquals(d, a.hadamardProduct(b));
        }
    }

    @Test
    public void testHadamardProduct_3() {
        for (Vector b: vs(3.0, 0.0, 0.0)) {
            Vector a = v(1.0, 0.0, 2.0);
            Vector c = v(3.0, 0.0, 0.0);

            Assert.assertEquals(c, a.hadamardProduct(b));
        }
    }

    @Test
    public void testMultiply_2_2x4() {
        for (Matrix b: ms(a(0.0, 5.0, 0.0, 6.0),
                          a(1.0, 0.0, 8.0, 0.0))) {

            Vector a = v(1.0, 2.0);
            Vector c = v(2.0, 5.0, 16.0, 6.0);

            Assert.assertEquals(c, a.multiply(b));
        }
    }

    @Test
    public void testMultiply_3_3x1() {
        for (Matrix b: ms(a(0.0),
                          a(3.0),
                          a(0.0))) {

            Vector a = v(0.0, 2.0, 0.0);
            Vector c = v(6.0);

            Assert.assertEquals(c, a.multiply(b));
        }
    }

    @Test
    public void testProduct_3() {
        Vector a = v(2.0, 4.0, 6.0);
        Assert.assertEquals(a.product(), 48.0, Vectors.EPS);
    }

    @Test
    public void testSum_3() {
        Vector a = v(2.0, 4.0, 6.0);
        Assert.assertEquals(a.sum(), 12.0, Vectors.EPS);
    }

    @Test
    public void testDivide_3() {
        Vector a = v(0.0, 0.0, 3.0);
        Vector b = v(0.0, 0.0, 0.3);

        Assert.assertEquals(b, a.divide(10));
    }

    @Test
    public void testCopy_5() {
        Vector a = v(0.0, 0.0, 0.0, 0.0, 1.0);
        Assert.assertEquals(a, a.copy());
    }

    @Test
    public void testBlank_5() {
        Vector a = v(0.0, 0.0, 0.0, 0.0, 1.0);
        Vector b = v(0.0, 0.0, 0.0, 0.0, 0.0);

        Assert.assertEquals(b, a.blank());
    }

    @Test
    public void testInnerProduct_1() {
        for (Vector b: vs(10.0)) {
            Vector a = v(18.0);
            Assert.assertEquals(180.0, a.innerProduct(b), Vectors.EPS);
        }
    }

    @Test
    public void testInnerProduct_3() {
        for (Vector b: vs(10.0, 0.0, 10.0)) {
            Vector a = v(1.0, 2.0, 3.0);
            Assert.assertEquals(40.0, a.innerProduct(b), Vectors.EPS);
        }
    }

    @Test
    public void testInnerProduct_4() {
        for (Vector b: vs(11.0, 13.0, 17.0, 19.0)) {
            Vector a = v(2.0, 3.0, 5.0, 7.0);
            Assert.assertEquals(279.0, a.innerProduct(b), Vectors.EPS);
        }
    }

    @Test
    public void testOuterProduct_3_4() {
        for (Vector b: vs(7.0, 11.0, 13.0, 17.0)) {
            Vector a = v(2.0, 3.0, 5.0);

            Matrix c = m(a(14.0, 22.0, 26.0, 34.0),
                         a(21.0, 33.0, 39.0, 51.0),
                         a(35.0, 55.0, 65.0, 85.0));

            Assert.assertEquals(c, a.outerProduct(b));
        }
    }

    @Test
    public void testOuterProduct_1_2() {
        for (Vector b: vs(24.0, 1.0)) {
            Vector a = v(2.0);
            Matrix c = m(a(48.0, 2.0));

            Assert.assertEquals(c, a.outerProduct(b));
        }
    }

    @Test
    public void testOuterProduct_4_2() {
        for (Vector b: vs(4.0, -10.0)) {
            Vector a = v(2.0, 0.0, -1.0, 41.0);

            Matrix c = m(a(8.0, -20.0),
                         a(0.0, 0.0),
                         a(-4.0, 10.0),
                         a(164.0, -410.0));

            Assert.assertEquals(c, a.outerProduct(b));
        }
    }

    /**
     * Tests whether two vectors contain the same elements
     *
     * @param vector1 Vector1
     * @param vector2 Vector2
     * @return True if both vectors contain the same elements
     */
    public boolean testWhetherVectorsContainSameElements(Vector vector1,
                                                         Vector vector2) {

        if (vector1.length() == vector2.length()) {

            boolean[] checkList = new boolean[vector1.length()];

            for (int i = 0; i < vector1.length(); i++) {
                for (int j = 0; j < vector2.length(); j++) {
                    if (vector1.get(i) == vector2.get(j)) {
                        if (!checkList[j]) {
                            checkList[j] = true;
                            break;
                        }
                    }
                }
            }

            boolean result = true;
            for (int i = 0; i < checkList.length; i++) {
                if (!checkList[i]) {
                    result = false;
                }
            }
            return result;
        } else {
            return false;
        }
    }

    @Test
    public void testTestWhetherVectorsContainSameElements() {
        Vector a = v(1.0, 1.0, 3.0, 4.0);
        Vector b = v(4.0, 1.0, 1.0, 3.0);
        Vector c = v(4.0, 2.0, 1.0, 3.0);

        Assert.assertTrue(testWhetherVectorsContainSameElements(a, b));
        Assert.assertFalse(testWhetherVectorsContainSameElements(a, c));
    }

    @Test
    public void testShuffle() {
        Vector a = v(1.0, 1.0, 3.0, 4.0);
        Vector b = a.shuffle();

        Assert.assertTrue(testWhetherVectorsContainSameElements(a, b));
    }

    @Test
    public void testMax() {
        Vector a = v(1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, -5.0, 0.0, 0.0, 5.0);
        Assert.assertEquals(5.0, a.max(), Vectors.EPS);
    }

    @Test
    public void testMaxCompressed() {
        Vector a = v(0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, -5.0, 0.0, 0.0);
        Assert.assertEquals(0.0, a.max(), Vectors.EPS);
    }

    @Test
    public void testMin() {
        Vector a = v(1.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, -5.0, 0.0, 0.0, 5.0);
        Assert.assertEquals(-5.0, a.min(), Vectors.EPS);
    }

    @Test
    public void testMinCompressed() {
        Vector a = v(1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.0);
        Assert.assertEquals(0.0, a.min(), Vectors.EPS);
    }

    @Test
    public void testFold_6() {
        Vector a = v(0.0, 0.0, 5.0, 0.0, 2.0, 1.0);

        VectorAccumulator sum = Vectors.asSumAccumulator(0.0);
        VectorAccumulator product = Vectors.asProductAccumulator(1.0);

        Assert.assertEquals(8.0, a.fold(sum), Vectors.EPS);
        // check whether the accumulator were flushed
        Assert.assertEquals(8.0, a.fold(sum), Vectors.EPS);

        Assert.assertEquals(0.0, a.fold(product), Vectors.EPS);
        // check whether the accumulator were flushed
        Assert.assertEquals(0.0, a.fold(product), Vectors.EPS);
    }

    @Test
    public void testIssue162_0() {
        VectorPredicate pi = new VectorPredicate() {
            @Override
            public boolean test(int i, double value) {
                return value == 3.14;
            }
        };

        Vector a = v();
        Vector b = a.copyOfLength(31);

        Assert.assertEquals(0, a.length());
        Assert.assertEquals(31, b.length());

        b.setAll(3.14);
        Assert.assertTrue(b.is(pi));

        Vector c = b.copyOfLength(42);
        c.setAll(3.14);
        Assert.assertTrue(c.is(pi));

        Vector d = c.copyOfLength(54);
        d.setAll(3.14);
        Assert.assertTrue(d.is(pi));
    }

    @Test
    public void testResize_32_to_110_to_1076_to_31() {
        VectorPredicate fortyTwo = new VectorPredicate() {
            @Override
            public boolean test(int i, double value) {
                return value == 42.0;
            }
        };

        Vector a = v();
        Vector b = a.copyOfLength(32);

        Assert.assertEquals(32, b.length());

        b.setAll(42.0);
        Assert.assertTrue(b.is(fortyTwo));

        Vector c = b.copyOfLength(110);
        c.setAll(42.0);
        Assert.assertTrue(c.is(fortyTwo));

        Vector d = c.copyOfLength(1076);
        d.setAll(42.0);
        Assert.assertTrue(d.is(fortyTwo));

        Vector e = d.copyOfLength(31);
        e.setAll(42.0);
        Assert.assertTrue(e.is(fortyTwo));
    }

    @Test
    public void testNormalize_Default() {
        Vector a = v(3.0, 0.0, -4.0);
        Vector b = a.divide(a.norm());

        Assert.assertEquals(3, b.length());
        Assert.assertEquals(0.6, b.get(0), Vectors.EPS);
        Assert.assertEquals(0.0, b.get(1), Vectors.EPS);
        Assert.assertEquals(-0.8, b.get(2), Vectors.EPS);
        // Verify b is a unit vector
        // The default normalize() uses Euclidean as the accumulator
        Assert.assertEquals(1.0, b.norm(), Vectors.EPS);
    }

    @Test
    public void testNormalize_EuclideanNormAccumulator() {
        Vector a = v(3.0, 0.0, -4.0);
        Vector b = a.divide(a.euclideanNorm());

        Assert.assertEquals(3, b.length());
        Assert.assertEquals(0.6, b.get(0), Vectors.EPS);
        Assert.assertEquals(0.0, b.get(1), Vectors.EPS);
        Assert.assertEquals(-0.8, b.get(2), Vectors.EPS);
        // Verify b is a unit vector
        Assert.assertEquals(1.0, b.euclideanNorm(), Vectors.EPS);
    }

    @Test
    public void testNormalize_ManhattanNormAccumulator() {
        Vector a = v(3.0, 0.0, -4.0);
        Vector b = a.divide(a.manhattanNorm());

        Assert.assertEquals(3, b.length());
        Assert.assertEquals(0.42857, b.get(0), 0.00001);
        Assert.assertEquals(0.0, b.get(1), Vectors.EPS);
        Assert.assertEquals(-0.57142, b.get(2), 0.00001);
        // Verify b is a unit vector
        Assert.assertEquals(1.0, b.manhattanNorm(), Vectors.EPS);
    }

    @Test
    public void testNormalize_InfinityNormAccumulator() {
        Vector a = v(3.0, 0.0, -4.0);
        Vector b = a.divide(a.infinityNorm());

        Assert.assertEquals(3, b.length());
        Assert.assertEquals(0.75, b.get(0), Vectors.EPS);
        Assert.assertEquals(0.0, b.get(1), Vectors.EPS);
        Assert.assertEquals(-1.0, b.get(2), Vectors.EPS);
        // Verify b is a unit vector
        Assert.assertEquals(1.0, b.infinityNorm(), Vectors.EPS);
    }

    @Test
    public void testEqualsWithPrecision() throws Exception {
        Vector a = v(1000.0, 1.0);
        Assert.assertTrue(a.equals(a, Vectors.EPS));

        Vector b = v().copyOfLength(1000);
        b.setAll(42.0);
        Assert.assertFalse(a.equals(b, Vectors.EPS));
        Assert.assertFalse(b.equals(a, Vectors.EPS));

        Vector e = v();
        Assert.assertTrue(e.equals(e, Vectors.EPS));

        Vector f = v(Double.MIN_VALUE, Double.MIN_VALUE);
        Vector g = vz(2);
        Assert.assertTrue(f.equals(g, Vectors.EPS));
        Assert.assertTrue(g.equals(f, Vectors.EPS));

        Vector i = v(Double.MIN_NORMAL, Double.MIN_NORMAL);
        Assert.assertTrue(i.equals(g, Vectors.EPS));
        Assert.assertTrue(i.equals(f, Vectors.EPS));
        Assert.assertTrue(g.equals(i, Vectors.EPS));
        Assert.assertTrue(f.equals(i, Vectors.EPS));
    }

    @Test
    public void testEquals() throws Exception {
        Vector a =  v().copyOfLength(1000);
        a.setAll(1.0);
        Assert.assertTrue(a.equals(a));
        Assert.assertTrue(a.copy().equals(a));

        Vector d = v().copyOfLength(1000);
        Assert.assertFalse(d.equals(a));
        Assert.assertTrue(d.equals(d.copy()));

        Vector e = v();
        Assert.assertTrue(e.equals(e.copy()));

        Vector f = v(Double.MIN_VALUE);
        Vector g = v(0.0);
        Assert.assertTrue(f.equals(g));
        Assert.assertTrue(g.equals(f));

        Vector i = v(Double.MIN_NORMAL);
        Assert.assertTrue(i.equals(g));
        Assert.assertTrue(i.equals(f));
        Assert.assertTrue(g.equals(i));
        Assert.assertTrue(f.equals(i));
    }

    @Test
    public void testFromCollection_empty() {
        List<Number> values = new LinkedList<>();
        Assert.assertEquals(Vector.fromCollection(values), Vector.zero(0));
    }

    @Test
    public void testFromCollection_normal_x3() {
        List<Double> values = Arrays.asList(1.0, 2.0, 3.0);
        Vector v = Vector.fromCollection(values);
        Assert.assertEquals(v, Vector.fromArray(new double[] {1.0, 2.0, 3.0}));
    }

    @Test
    public void testFromCollection_byte() {
        List<Byte> values = Arrays.asList((byte) 1, (byte) 3, (byte) 5, (byte) 6);
        Vector v = Vector.fromCollection(values);
        Assert.assertEquals(v, Vector.fromArray(new double[] {1.0, 3.0, 5.0, 6.0}));
    }

    @Test(expected = NullPointerException.class)
    public void testFromCollection_NPE() {
        Vector v = Vector.fromCollection(null);
    }

    @Test
    public void testFromMap_empty() {
        Map<Integer, Double> map = new HashMap<>();
        Assert.assertEquals(Vector.fromMap(map, 0), Vector.zero(0));
    }

    @Test
    public void testFromMap_normal() {
        Map<Integer, Double> map = new HashMap<>();
        map.put(0, 1.0);
        map.put(3, 2.0);
        map.put(5, 1.0);
        Vector v = Vector.fromArray(new double[]{1, 0, 0, 2, 0, 1, 0});
        Assert.assertEquals(v, Vector.fromMap(map, 7));
    }

    @Test
    public void testFromMap_emptyMap() {
        Map<Integer, Double> map = new HashMap<>();
        Assert.assertEquals(Vector.fromMap(map, 5), Vector.zero(5));
    }

    @Test(expected = IllegalArgumentException.class)
    public void testFromMap_invalidMap() {
        Map<Integer, Double> map = new HashMap<>();
        map.put(0, 1.0);
        map.put(3, 2.0);
        map.put(-2, 1.0);
        Vector v = Vector.fromMap(map, 5);
    }

    @Test(expected = NullPointerException.class)
    public void testFromMap_NPE() {
        Vector v = Vector.fromMap(null, 4);
    }
}
