package com.adgaudio.ApproxDictMatch;

import org.junit.Test;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;

/**
 * Created by Alex Gaudio on 1/30/17.
 */
public class FreqDistTest {
    private final Collection<String> fixture1 = Arrays.asList("a", "b", "a", "b", "c", "d");
    private final Collection<String> fixture2 = Arrays.asList("a", "b", "a", "c", "e");

    @Test
    public void testConstructor1() {
        Map<String, Double> expected = new HashMap<>();
        expected.put("a", 2.0);
        expected.put("b", 2.0);
        expected.put("c", 1.0);
        expected.put("d", 1.0);

        FreqDist<String> fd = new FreqDist<>(fixture1);
        assertEquals(expected, fd);
    }

    @Test
    public void testConstructor2() {
        Map<String, Double> expected = new HashMap<>();
        expected.put("a", 4.0);
        expected.put("b", 3.0);
        expected.put("c", 2.0);
        expected.put("d", 1.0);
        expected.put("e", 1.0);

        FreqDist<String> fd = new FreqDist<>(Arrays.asList(fixture1, fixture2), null);
        assertEquals(expected, fd);
    }

    @Test
    public void testConstructor3() {
        // empty constructor
        assertEquals(new HashMap<String, Double>(), new FreqDist<String>());
    }

    @Test
    public void testUpdate1() {
        FreqDist<String> fd1 = new FreqDist<>(fixture1);
        FreqDist<String> fd2 = new FreqDist<>(fixture2);
        fd1.update(fd2, FreqDist.updateAdd);

        Map<String, Double> expected = new HashMap<>();
        expected.put("a", 4.0);
        expected.put("b", 3.0);
        expected.put("c", 2.0);
        expected.put("d", 1.0);
        expected.put("e", 1.0);
        assertEquals(expected, fd1);
        assertTrue(expected.equals(fd1));  // sanity check
        assertFalse(fd1.equals(fd2));
    }

    @Test
    public void testUpdate2() {
        FreqDist<String> fd1 = new FreqDist<>(fixture1);
        fd1.update("a", 1.5, FreqDist.updateAdd);
        fd1.update("b", 4.0, FreqDist.updateDivide);
        fd1.update("z", -10.0, FreqDist.updateAdd);
        fd1.update("y", -10.0, FreqDist.updateDivide);

        Map<String, Double> expected = new HashMap<>();
        expected.put("a", 3.5);
        expected.put("b", 0.5);
        expected.put("c", 1.0);
        expected.put("d", 1.0);
        expected.put("z", -10.0);
        expected.put("y", -0.0);

        assertEquals(expected, fd1);
    }

    @Test
    public void testUpdate3() {
        FreqDist<String> fd1 = new FreqDist<>(fixture1);
        FreqDist<String> fd2 = new FreqDist<>(fixture2);
        fd1.update(Arrays.asList("a", "b"), fd2, FreqDist.updateAdd);

        Map<String, Double> expected = new HashMap<>();
        expected.put("a", 4.0);
        expected.put("b", 3.0);
        expected.put("c", 1.0);
        expected.put("d", 1.0);
        assertEquals(expected, fd1);
        assertTrue(expected.equals(fd1));  // sanity check
        assertFalse(fd1.equals(fd2));
    }

    @Test
    public void testMergeFunc() {
        class SubtractPlusOneTenth implements FreqDist.UpdateFunc {
            public Double apply(Double a, Double b) { return a - b + 0.1; }
        }
        FreqDist<String> fd1 = new FreqDist<>(fixture1);
        FreqDist<String> fd2 = new FreqDist<>(fixture2);
        fd1.update(fd2, new SubtractPlusOneTenth());

        Map<String, Double> expected = new HashMap<>();
        expected.put("a", 0.1);
        expected.put("b", 1.1);
        expected.put("c", 0.1);
        expected.put("d", 1.0);  // untouched because "d" not on fd2
        expected.put("e", -0.9);
        assertEquals(expected, fd1);
    }

    @Test
    public void testNormalizeOverSum() {
        FreqDist<String> fd1 = new FreqDist<>(fixture1);
        fd1.normalizeOverSum();

        Map<String, Double> expected = new HashMap<>();
        expected.put("a", 2.0 / 6.0 );
        expected.put("b", 2.0 / 6.0 );
        expected.put("c", 1.0 / 6.0 );
        expected.put("d", 1.0 / 6.0 );

        assertEquals(expected, fd1);
    }
}