/*
 * Copyright 2013 Proofpoint, Inc.
 *
 * 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.
 */
package io.airlift.stats;

import io.airlift.stats.TimeStat.BlockTimer;
import io.airlift.testing.TestingTicker;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

import static com.google.common.math.DoubleMath.fuzzyEquals;
import static java.lang.Math.min;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;


public class TestTimeStat
{
    private static final int VALUES = 1000;
    private TestingTicker ticker;

    @BeforeMethod
    public void setup()
    {
        ticker = new TestingTicker();
    }

    @Test
    public void testBasic()
    {
        TimeStat stat = new TimeStat();
        List<Long> values = new ArrayList<>(VALUES);
        for (long i = 0; i < VALUES; i++) {
            values.add(i);
        }
        Collections.shuffle(values);
        for (Long value : values) {
            stat.add(value, TimeUnit.MILLISECONDS);
        }
        Collections.sort(values);

        TimeDistribution allTime = stat.getAllTime();
        assertEquals(allTime.getCount(), (double)values.size());
        assertTrue(fuzzyEquals(allTime.getMax(), values.get(values.size() - 1) * 0.001, 0.000_000_000_1));
        assertEquals(allTime.getMin(), values.get(0) * 0.001);
        assertEquals(allTime.getUnit(), TimeUnit.SECONDS);

        assertPercentile("tp50", allTime.getP50(), values, 0.50);
        assertPercentile("tp50", allTime.getP75(), values, 0.75);
        assertPercentile("tp90", allTime.getP90(), values, 0.90);
        assertPercentile("tp99", allTime.getP99(), values, 0.99);
    }



    @Test
    public void testEmpty()
    {
        TimeStat stat = new TimeStat();
        TimeDistribution allTime = stat.getAllTime();
        assertEquals(allTime.getMin(), Double.NaN);
        assertEquals(allTime.getMax(), Double.NaN);
        assertEquals(allTime.getP50(), Double.NaN);
        assertEquals(allTime.getP75(), Double.NaN);
        assertEquals(allTime.getP90(), Double.NaN);
        assertEquals(allTime.getP99(), Double.NaN);
    }

    @Test
    public void time()
            throws Exception
    {
        TimeStat stat = new TimeStat(ticker);
        stat.time(new Callable<Void>()
        {
            @Override
            public Void call()
            {
                ticker.increment(10, TimeUnit.MILLISECONDS);
                return null;
            }
        });

        TimeDistribution allTime = stat.getAllTime();
        assertEquals(allTime.getCount(), 1.0);
        assertEquals(allTime.getMin(), 0.010);
        assertEquals(allTime.getMax(), 0.010);
    }

    @Test
    public void timeTry()
            throws Exception
    {
        TimeStat stat = new TimeStat(ticker);
        try (BlockTimer ignored = stat.time()) {
            ticker.increment(10, TimeUnit.MILLISECONDS);
        }

        TimeDistribution allTime = stat.getAllTime();
        assertEquals(allTime.getCount(), 1.0);
        assertEquals(allTime.getMin(), 0.010);
        assertEquals(allTime.getMax(), 0.010);
    }

    @Test
    public void testUnit()
    {
        TimeStat stat = new TimeStat(ticker, TimeUnit.MILLISECONDS);
        stat.add(1, TimeUnit.SECONDS);

        TimeDistribution allTime = stat.getAllTime();
        assertEquals(allTime.getMin(), 1000.0);
        assertEquals(allTime.getMax(), 1000.0);
    }

    private static void assertPercentile(String name, double value, List<Long> values, double percentile)
    {
        int index = (int) (values.size() * percentile);
        assertBounded(name, value, values.get(index - 1) * 0.001, values.get(min(index + 1, values.size() - 1)) * 0.001);
    }

    private static void assertBounded(String name, double value, double minValue, double maxValue)
    {
        if (value >= minValue && value <= maxValue) {
            return;
        }

        fail(String.format("%s expected:<%s> to be between <%s> and <%s>", name, value, minValue, maxValue));
    }
}

@@ -2193,34 +2193,34 @@
 rtPercentile(%22tp
+7
 5
-0
 %22, allTime.getP7

Fix typo