import numpy as np

import pandas.util.testing as tm
from pandas import compat
from pandas._libs import algos as libalgos, index as libindex


class TestNumericEngine(object):
    def test_is_monotonic(self, numeric_indexing_engine_type_and_dtype):
        engine_type, dtype = numeric_indexing_engine_type_and_dtype
        num = 1000
        arr = np.array([1] * num + [2] * num + [3] * num, dtype=dtype)

        # monotonic increasing
        engine = engine_type(lambda: arr, len(arr))
        assert engine.is_monotonic_increasing is True
        assert engine.is_monotonic_decreasing is False

        # monotonic decreasing
        engine = engine_type(lambda: arr[::-1], len(arr))
        assert engine.is_monotonic_increasing is False
        assert engine.is_monotonic_decreasing is True

        # neither monotonic increasing or decreasing
        arr = np.array([1] * num + [2] * num + [1] * num, dtype=dtype)
        engine = engine_type(lambda: arr[::-1], len(arr))
        assert engine.is_monotonic_increasing is False
        assert engine.is_monotonic_decreasing is False

    def test_is_unique(self, numeric_indexing_engine_type_and_dtype):
        engine_type, dtype = numeric_indexing_engine_type_and_dtype

        # unique
        arr = np.array([1, 3, 2], dtype=dtype)
        engine = engine_type(lambda: arr, len(arr))
        assert engine.is_unique is True

        # not unique
        arr = np.array([1, 2, 1], dtype=dtype)
        engine = engine_type(lambda: arr, len(arr))
        assert engine.is_unique is False

    def test_get_loc(self, numeric_indexing_engine_type_and_dtype):
        engine_type, dtype = numeric_indexing_engine_type_and_dtype

        # unique
        arr = np.array([1, 2, 3], dtype=dtype)
        engine = engine_type(lambda: arr, len(arr))
        assert engine.get_loc(2) == 1

        # monotonic
        num = 1000
        arr = np.array([1] * num + [2] * num + [3] * num, dtype=dtype)
        engine = engine_type(lambda: arr, len(arr))
        assert engine.get_loc(2) == slice(1000, 2000)

        # not monotonic
        arr = np.array([1, 2, 3] * num, dtype=dtype)
        engine = engine_type(lambda: arr, len(arr))
        expected = np.array([False, True, False] * num, dtype=bool)
        result = engine.get_loc(2)
        assert (result == expected).all()

    def test_get_backfill_indexer(
            self, numeric_indexing_engine_type_and_dtype):
        engine_type, dtype = numeric_indexing_engine_type_and_dtype

        arr = np.array([1, 5, 10], dtype=dtype)
        engine = engine_type(lambda: arr, len(arr))

        new = np.array(compat.range(12), dtype=dtype)
        result = engine.get_backfill_indexer(new)

        expected = libalgos.backfill(arr, new)
        tm.assert_numpy_array_equal(result, expected)

    def test_get_pad_indexer(
            self, numeric_indexing_engine_type_and_dtype):
        engine_type, dtype = numeric_indexing_engine_type_and_dtype

        arr = np.array([1, 5, 10], dtype=dtype)
        engine = engine_type(lambda: arr, len(arr))

        new = np.array(compat.range(12), dtype=dtype)
        result = engine.get_pad_indexer(new)

        expected = libalgos.pad(arr, new)
        tm.assert_numpy_array_equal(result, expected)


class TestObjectEngine(object):
    engine_type = libindex.ObjectEngine
    dtype = np.object_
    values = list('abc')

    def test_is_monotonic(self):

        num = 1000
        arr = np.array(['a'] * num + ['a'] * num + ['c'] * num,
                       dtype=self.dtype)

        # monotonic increasing
        engine = self.engine_type(lambda: arr, len(arr))
        assert engine.is_monotonic_increasing is True
        assert engine.is_monotonic_decreasing is False

        # monotonic decreasing
        engine = self.engine_type(lambda: arr[::-1], len(arr))
        assert engine.is_monotonic_increasing is False
        assert engine.is_monotonic_decreasing is True

        # neither monotonic increasing or decreasing
        arr = np.array(['a'] * num + ['b'] * num + ['a'] * num,
                       dtype=self.dtype)
        engine = self.engine_type(lambda: arr[::-1], len(arr))
        assert engine.is_monotonic_increasing is False
        assert engine.is_monotonic_decreasing is False

    def test_is_unique(self):
        # unique
        arr = np.array(self.values, dtype=self.dtype)
        engine = self.engine_type(lambda: arr, len(arr))
        assert engine.is_unique is True

        # not unique
        arr = np.array(['a', 'b', 'a'], dtype=self.dtype)
        engine = self.engine_type(lambda: arr, len(arr))
        assert engine.is_unique is False

    def test_get_loc(self):
        # unique
        arr = np.array(self.values, dtype=self.dtype)
        engine = self.engine_type(lambda: arr, len(arr))
        assert engine.get_loc('b') == 1

        # monotonic
        num = 1000
        arr = np.array(['a'] * num + ['b'] * num + ['c'] * num,
                       dtype=self.dtype)
        engine = self.engine_type(lambda: arr, len(arr))
        assert engine.get_loc('b') == slice(1000, 2000)

        # not monotonic
        arr = np.array(self.values * num, dtype=self.dtype)
        engine = self.engine_type(lambda: arr, len(arr))
        expected = np.array([False, True, False] * num, dtype=bool)
        result = engine.get_loc('b')
        assert (result == expected).all()

    def test_get_backfill_indexer(self):
        arr = np.array(['a', 'e', 'j'], dtype=self.dtype)
        engine = self.engine_type(lambda: arr, len(arr))

        new = np.array(list('abcdefghij'), dtype=self.dtype)
        result = engine.get_backfill_indexer(new)

        expected = libalgos.backfill_object(arr, new)
        tm.assert_numpy_array_equal(result, expected)

    def test_get_pad_indexer(self):
        arr = np.array(['a', 'e', 'j'], dtype=self.dtype)
        engine = self.engine_type(lambda: arr, len(arr))

        new = np.array(list('abcdefghij'), dtype=self.dtype)
        result = engine.get_pad_indexer(new)

        expected = libalgos.pad_object(arr, new)
        tm.assert_numpy_array_equal(result, expected)
