import numpy as np

import pandas as pd
import pandas._testing as tm


class TestEquals:
    def test_dataframe_not_equal(self):
        # see GH#28839
        df1 = pd.DataFrame({"a": [1, 2], "b": ["s", "d"]})
        df2 = pd.DataFrame({"a": ["s", "d"], "b": [1, 2]})
        assert df1.equals(df2) is False

    def test_equals_object_array_vs_scalar(self):
        # GH#43867 one object-dtype element is a numpy array, the other a
        #  scalar; these must not compare equal
        df = pd.DataFrame({"x": ["a"]}, dtype=object)
        other = df.copy()
        other["x"] = [np.array(["a"], dtype=object)]
        assert df["x"].dtype == other["x"].dtype == object
        assert not df.equals(other)
        assert not other.equals(df)

    def test_equals_different_blocks(self):
        # GH#9330
        df0 = pd.DataFrame(
            {"A": [1.0, 2.0], "B": np.array([1, 2], dtype=np.int64), "C": [3.0, 4.0]}
        )
        # build df1 via sequential __setitem__ so the float columns end up
        # in separate blocks instead of being consolidated upfront
        df1 = pd.DataFrame({"A": [1.0, 2.0]})
        df1["B"] = np.array([1, 2], dtype=np.int64)
        df1["C"] = np.array([3.0, 4.0])
        assert len(df0._mgr.blocks) == 2
        assert len(df1._mgr.blocks) == 3

        # do the real tests
        tm.assert_frame_equal(df0, df1)
        assert df0.equals(df1)
        assert df1.equals(df0)

    def test_equals(self):
        # Add object dtype column with nans
        index = np.random.default_rng(2).random(10)
        df1 = pd.DataFrame(
            np.random.default_rng(2).random(10), index=index, columns=["floats"]
        )
        df1["text"] = "the sky is so blue. we could use more chocolate.".split()
        df1["start"] = pd.date_range("2000-1-1", periods=10, freq="min")
        df1["end"] = pd.date_range("2000-1-1", periods=10, freq="D")
        df1["diff"] = df1["end"] - df1["start"]
        # Explicitly cast to object, to avoid implicit cast when setting np.nan
        df1["bool"] = (np.arange(10) % 3 == 0).astype(object)
        df1.loc[::2] = np.nan
        df2 = df1.copy()
        assert df1["text"].equals(df2["text"])
        assert df1["start"].equals(df2["start"])
        assert df1["end"].equals(df2["end"])
        assert df1["diff"].equals(df2["diff"])
        assert df1["bool"].equals(df2["bool"])
        assert df1.equals(df2)
        assert not df1.equals(object)

        # different dtype
        different = df1.copy()
        different["floats"] = different["floats"].astype("float32")
        assert not df1.equals(different)

        # different index
        different_index = -index
        different = df2.set_index(different_index)
        assert not df1.equals(different)

        # different columns
        different = df2.copy()
        different.columns = df2.columns[::-1]
        assert not df1.equals(different)

        # DatetimeIndex
        index = pd.date_range("2000-1-1", periods=10, freq="min")
        df1 = df1.set_index(index)
        df2 = df1.copy()
        assert df1.equals(df2)

        # MultiIndex
        df3 = df1.set_index(["text"], append=True)
        df2 = df1.set_index(["text"], append=True)
        assert df3.equals(df2)

        df2 = df1.set_index(["floats"], append=True)
        assert not df3.equals(df2)

        # NaN in index
        df3 = df1.set_index(["floats"], append=True)
        df2 = df1.set_index(["floats"], append=True)
        assert df3.equals(df2)

    def test_equals_categorical_categories_order(self):
        cat1 = pd.Categorical(["a", "b", "a"], categories=["a", "b"])
        cat2 = pd.Categorical(["a", "b", "a"], categories=["b", "a"])
        df1 = pd.DataFrame({"c": cat1})
        df2 = pd.DataFrame({"c": cat2})

        assert df1.equals(df2)

        cat3 = pd.Categorical(["a", "b", "a"], categories=["a", "b", "c"])
        df3 = pd.DataFrame({"c": cat3})

        assert not df1.equals(df3)
