"""
This file was modified from CPython.
Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
2011, 2012, 2013, 2014, 2015, 2016 Python Software Foundation; All Rights Reserved
"""
import copy, types
# import copy_reg
# import weakref
# import abc
from operator import le, lt, ge, gt, eq, ne

import unittest

order_comparisons = le, lt, ge, gt
equality_comparisons = eq, ne
comparisons = order_comparisons + equality_comparisons
n = []
class _deep_reduce_ex:
    def __reduce_ex__(self, proto):
        n.append(1)
        print "append to n"
        return ""
    def __reduce__(self):
        self.fail("shouldn't call this")

class Deepcopy_Deepcopy:
    def __init__(self, foo):
        self.foo = foo
    def __deepcopy__(self, memo=None):
        return Deepcopy_Deepcopy(self.foo)
e = []

class deep_reduce:
    def __reduce__(self):
        e.append(1)
        print "append to e"
        return ""

class Inst_Deepcopy:
    def __init__(self, foo):
        self.foo = foo
    def __deepcopy__(self, memo):
        return Inst_Deepcopy(copy.deepcopy(self.foo, memo))
    def __eq__(self, other):
        return self.foo == other.foo

class reconstruct_nostate(object):
    def __reduce__(self):
        return (reconstruct_nostate, ())

class reconstruct_state():
    def __reduce__(self):
        return (reconstruct_state, (), self.__dict__)
    def __eq__(self, other):
        return self.__dict__ == other.__dict__

class reconstruct_state_setstate(object):
    def __reduce__(self):
        return (reconstruct_state_setstate, (), self.__dict__)
    def __setstate__(self, state):
        self.__dict__.update(state)
    def __eq__(self, other):
        return self.__dict__ == other.__dict__

class reduce_4tuple(list):
    def __reduce__(self):
        return (reduce_4tuple, (), self.__dict__, iter(self))
    def __eq__(self, other):
        return (list(self) == list(other) and
                self.__dict__ == other.__dict__)

class reduce_5tuple(dict):
    def __reduce__(self):
        return (reduce_5tuple, (), self.__dict__, None, self.items())
    def __eq__(self, other):
        return (dict(self) == dict(other) and
                self.__dict__ == other.__dict__)

class TestCopy(unittest.TestCase):
# # The deepcopy() method

    def test_deepcopy_basic(self):
        x = 42
        y = copy.deepcopy(x)
        self.assertEqual(y, x)

    def test_deepcopy_memo(self):
        # Tests of reflexive objects are under type-specific sections below.
        # This tests only repetitions of objects.
        x = []
        x = [x, x]
        y = copy.deepcopy(x)
        self.assertEqual(y, x)
        self.assertIsNot(y, x)
        self.assertIsNot(y[0], x[0])
        self.assertIs(y[0], y[1])

    def test_deepcopy_issubclass(self):
        # XXX Note: there's no way to test the TypeError coming out of
        # issubclass() -- this can only happen when an extension
        # module defines a "type" that doesn't formally inherit from
        # type.
        # class Meta(type):
        class Meta():
            pass
        class C(Meta):
            pass
        self.assertEqual(copy.deepcopy(C), C)

    def test_deepcopy_deepcopy(self):
        x = Deepcopy_Deepcopy(42)
        y = copy.deepcopy(x)
        self.assertEqual(y.__class__, x.__class__)
        self.assertEqual(y.foo, x.foo)

    # def test_deepcopy_registry(self):
    #   class C(object):
    #       def __new__(cls, foo):
    #           obj = object.__new__(cls)
    #           obj.foo = foo
    #           return obj
    #   def pickle_C(obj):
    #       return (C, (obj.foo,))
    #   x = C(42)
    #   self.assertRaises(TypeError, copy.deepcopy, x)
    #   # copy_reg.pickle(C, pickle_C, C)
    #   # y = copy.deepcopy(x)

    def test_deepcopy_reduce_ex(self):
        x = _deep_reduce_ex()
        y = copy.deepcopy(x)
        self.assertEqual(n, [])

    # def test_deepcopy_reduce(self):
    #   x = deep_reduce()
    #   y = copy.deepcopy(x)
    #   self.assertIs(y, x)
    #   self.assertEqual(e, [1])

    # def test_deepcopy_cant(self):
    #   class C:
    #       def __getattribute__(self, name):
    #           if name.startswith("__reduce"):
    #               raise AttributeError(name)
    #           return object.__getattribute__(self, name)
    #   x = C()
    #   # print "Should have error here", copy.deepcopy(x)
    #   self.assertRaises(TypeError, copy.deepcopy, x)

    # # Type-specific _deepcopy_xxx() methods

    def test_deepcopy_atomic(self):
        class Classic:
            pass
        class NewStyle(object):
            pass
        def f():
            pass
        tests = [None, 42, 3.14, True, False, "hello"]
        # not implemented yet : f.__code__, 2**100, 1j, NewStyle, max, "hello\u1234", Classic] 
        for x in tests:
            self.assertIs(copy.deepcopy(x), x)

    def test_deepcopy_list(self):
        x = [[1, 2], 3]
        y = copy.deepcopy(x)
        self.assertEqual(y, x)
        self.assertIsNot(x, y)
        self.assertIsNot(x[0], y[0])

    def test_deepcopy_reflexive_list(self):
        x = []
        x.append(x)
        y = copy.deepcopy(x)
        # for op in comparisons:
        #     self.assertRaises(RecursionError, op, y, x)
        self.assertIsNot(y, x)
        self.assertIs(y[0], y)
        self.assertEqual(len(y), 1)

    def test_deepcopy_set(self):
        class FooSet:
            def bar(self):
                pass
        a = set([1,2,3])
        b = copy.deepcopy(a)
        a = set([1,2,3])
        b = copy.deepcopy(a)
        self.assertTrue(a == b)
        self.assertFalse(a is b)
        mixed = set([(1,2), FooSet.bar])
        mixedcopy = copy.deepcopy(mixed)
        self.assertTrue(mixed == mixedcopy)
        self.assertFalse(mixed is mixedcopy)
        mixed.add(9)
        mixedcopy2 = copy.deepcopy(mixed)
        self.assertTrue(mixed == mixedcopy2)
        self.assertFalse(mixed is mixedcopy2)

    def test_deepcopy_empty_tuple(self):
        x = ()
        y = copy.deepcopy(x)
        self.assertIs(x, y)

    def test_deepcopy_tuple(self):
        x = ([1, 2], 3)
        y = copy.deepcopy(x)
        self.assertEqual(y, x)
        self.assertIsNot(x, y)
        self.assertIsNot(x[0], y[0])

    def test_deepcopy_tuple_of_immutables(self):
        x = ((1, 2), 3)
        y = copy.deepcopy(x)
        self.assertIs(x, y)

    # def test_deepcopy_reflexive_tuple(self):
    #   x = ([],)
    #   x[0].append(x)
    #   print "here is x", x

    #   print type(x[0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0][0])
    #   y = copy.deepcopy(x)
    #   # for op in comparisons:
    #   #     self.assertRaises(RecursionError, op, y, x)
    #   self.assertIsNot(y, x)
    #   # self.assertIsNot(y[0], x[0])
    #   # self.assertIs(y[0][0], y)

    def test_deepcopy_dict(self):
        x = {"foo": [1, 2], "bar": 3}
        y = copy.deepcopy(x)
        self.assertEqual(y, x)
        self.assertIsNot(x, y)
        self.assertIsNot(x["foo"], y["foo"])

    def test_deepcopy_reflexive_dict(self):
        x = {}
        x['foo'] = x
        y = copy.deepcopy(x)
        # for op in order_comparisons:
        #     self.assertRaises(TypeError, op, y, x)
        # for op in equality_comparisons:
            # self.assertRaises(RecursionError, op, y, x)
        self.assertIsNot(y, x)
        self.assertIs(y['foo'], y)
        self.assertEqual(len(y), 1)

    def test_deepcopy_keepalive(self):
        memo = {}
        x = []
        y = copy.deepcopy(x, memo)
        self.assertIs(memo[id(memo)][0], x)

    def test_deepcopy_dont_memo_immutable(self):
        memo = {}
        x = [1, 2, 3, 4]
        y = copy.deepcopy(x, memo)
        self.assertEqual(y, x)
        # There's the entry for the new list, and the keep alive.
        self.assertEqual(len(memo), 6)

        memo = {}
        x = [(1, 2)]
        y = copy.deepcopy(x, memo)
        self.assertEqual(y, x)
        # Tuples with immutable contents are immutable for deepcopy.
        self.assertEqual(len(memo), 5)

        memo = {}
        n = [False, True, None, "string"]
        o = copy.deepcopy(n, memo)
        self.assertEqual(len(memo), 6)

    def test_deepcopy_inst_vanilla(self):
        class C:
            def __init__(self, foo):
                self.foo = foo
            def __eq__(self, other):
                return self.foo == other.foo
        x = C([42])
        y = copy.deepcopy(x)
        self.assertEqual(y, x)
        self.assertIsNot(y.foo, x.foo)
        # print "types", type(y), type(x), x.__class__, y.__class__

    def test_deepcopy_inst_deepcopy(self):
        x = Inst_Deepcopy([42])
        y = copy.deepcopy(x)
        self.assertEqual(y, x)
        self.assertIsNot(y, x)
        self.assertIsNot(y.foo, x.foo)

    def test_deepcopy_inst_getinitargs(self):
        class C:
            def __init__(self, foo):
                self.foo = foo
            def __getinitargs__(self):
                return (self.foo,)
            def __eq__(self, other):
                return self.foo == other.foo
        x = C([42])
        self.assertRaises(NotImplementedError, copy.deepcopy, x)

        # y = copy.deepcopy(x)
        # self.assertEqual(y, x)
        # self.assertIsNot(y, x)
        # self.assertIsNot(y.foo, x.foo)

#   # def test_deepcopy_inst_getnewargs(self):
#   #   class C(int):
#   #       def __new__(cls, foo):
#   #           self = int.__new__(cls)
#   #           self.foo = foo
#   #           return self
#   #       def __getnewargs__(self):
#   #           return self.foo,
#   #       def __eq__(self, other):
#   #           return self.foo == other.foo
#   #   x = C(42)
#   #   y = copy.deepcopy(x)
#   #   self.assertIsInstance(y, C)
#   #   print isinstance(y, C)
#   #   self.assertEqual(y, x)
#   #   self.assertIsNot(y, x)
#   #   self.assertEqual(y.foo, x.foo)
#   #   self.assertIsNot(y.foo, x.foo)

#   # def test_deepcopy_inst_getnewargs_ex(self):
#   #   class C(int):
#   #       def __new__(cls, *, foo):
#   #           self = int.__new__(cls)
#   #           self.foo = foo
#   #           return self
#   #       def __getnewargs_ex__(self):
#   #           return (), {'foo': self.foo}
#   #       def __eq__(self, other):
#   #           return self.foo == other.foo
#   #   x = C(foo=[42])
#   #   y = copy.deepcopy(x)
#   #   self.assertIsInstance(y, C)
#   #   self.assertEqual(y, x)
#   #   self.assertIsNot(y, x)
#   #   self.assertEqual(y.foo, x.foo)
#   #   self.assertIsNot(y.foo, x.foo)

#   def test_deepcopy_inst_getstate(self):
#       class C:
#           def __init__(self, foo):
#               self.foo = foo
#           def __getstate__(self):
#               return {"foo": self.foo}
#           def __eq__(self, other):
#               return self.foo == other.foo
#       x = C([42])
#       y = copy.deepcopy(x)
#       self.assertEqual(y, x)
#       self.assertIsNot(y, x)
#       self.assertIsNot(y.foo, x.foo)

#   def test_deepcopy_inst_setstate(self):
#       class C:
#           def __init__(self, foo):
#               self.foo = foo
#           def __setstate__(self, state):
#               self.foo = state["foo"]
#           def __eq__(self, other):
#               return self.foo == other.foo
#       x = C([42])
#       y = copy.deepcopy(x)
#       self.assertEqual(y, x)
#       self.assertIsNot(y, x)
#       self.assertIsNot(y.foo, x.foo)

#   # def test_deepcopy_inst_getstate_setstate(self):
#   #   class C:
#   #       def __init__(self, foo):
#   #           self.foo = foo
#   #       def __getstate__(self):
#   #           return self.foo
#   #       def __setstate__(self, state):
#   #           self.foo = state
#   #       def __eq__(self, other):
#   #           return self.foo == other.foo
#   #   x = C([42])
#   #   y = copy.deepcopy(x)
#   #   self.assertEqual(y, x)
#   #   self.assertIsNot(y, x)
#   #   self.assertIsNot(y.foo, x.foo)
#   #   # State with boolean value is false (issue #25718)
#   #   x = C([])
#   #   y = copy.deepcopy(x)
#   #   self.assertEqual(y, x)
#   #   self.assertIsNot(y, x)
#   #   self.assertIsNot(y.foo, x.foo)

#   def test_deepcopy_reflexive_inst(self):
#       class C:
#           pass
#       x = C()
#       x.foo = x
#       y = copy.deepcopy(x)
#       self.assertIsNot(y, x)
#       self.assertIs(y.foo, y)

    # def test_deepcopy_range(self):
    #   class I(int):
    #       pass
    #   x = range(I(10))
    #   y = copy.deepcopy(x)
    #   self.assertIsNot(y, x)
    #   self.assertEqual(y, x)
    #   self.assertIsNot(y.stop, x.stop)
    #   self.assertEqual(y.stop, x.stop)
    #   self.assertIsInstance(y.stop, I)

#   # # _reconstruct()

    # def test_reconstruct_string(self):
    #   class C:
    #       def __reduce__(self):
    #           return ""
    #   x = C()
    #   y = copy.copy(x)
        # self.assertIs(y, x)
        # y = copy.deepcopy(x)
        # self.assertIs(y, x)


    def test_reconstruct_nostate(self):
        x = reconstruct_nostate()
        x.foo = 42
        y = copy.copy(x)
        # self.assertIs(y.__class__, x.__class__)
        y = copy.deepcopy(x)
        # self.assertIs(y.__class__, x.__class__)

    def test_reconstruct_state(self):
        x = reconstruct_state()
        x.foo = [42]
        y = copy.copy(x)
        # self.assertEqual(y, x)
        y = copy.deepcopy(x)
        # self.assertEqual(y, x)
        self.assertIsNot(y.foo, x.foo)

    def test_reconstruct_state_setstate(self):
        x = reconstruct_state_setstate()
        x.foo = [42]
        # y = copy.copy(x)
        self.assertRaises(NotImplementedError, copy.copy, x)
        # self.assertEqual(y, x)
        # y = copy.deepcopy(x)
        # self.assertEqual(y, x)
        # self.assertIsNot(y.foo, x.foo)

    def test_reconstruct_reflexive(self):
        class C(object):
            pass
        x = C()
        x.foo = x
        y = copy.deepcopy(x)
        self.assertIsNot(y, x)
        self.assertIs(y.foo, y)

#     # Additions for Python 2.3 and pickle protocol 2

    # def test_reduce_4tuple(self):
    #   x = reduce_4tuple([[1, 2], 3])
    #   y = copy.copy(x)
    #   self.assertEqual(x, y)
    #   self.assertIsNot(x, y)
    #   self.assertIs(x[0], y[0])
    #   y = copy.deepcopy(x)
    #   self.assertEqual(x, y)
    #   self.assertIsNot(x, y)
    #   self.assertIsNot(x[0], y[0])

    # def test_reduce_5tuple(self):

    #   x = reduce_5tuple([("foo", [1, 2]), ("bar", 3)])
    #   y = copy.copy(x)
    #   # self.assertEqual(x, y)
    #   self.assertIsNot(x, y)
    #   self.assertIs(x["foo"], y["foo"])
    #   y = copy.deepcopy(x)
    #   # self.assertEqual(x, y)
    #   self.assertIsNot(x, y)
    #   self.assertIsNot(x["foo"], y["foo"])

    def test_copy_slots(self):
        class C:
            __slots__ = ["foo"]
        x = C()
        x.foo = [42]
        y = copy.copy(x)
        self.assertIs(x.foo, y.foo)

    def test_deepcopy_slots(self):
        class C(object):
            __slots__ = ["foo"]
        x = C()
        x.foo = [42]
        y = copy.deepcopy(x)
        self.assertEqual(x.foo, y.foo)
        self.assertIsNot(x.foo, y.foo)

    # def test_deepcopy_dict_subclass(self):
    #   class C(dict):
    #       def __init__(self, d=None):
    #           if not d:
    #               d = {}
    #           self._keys = list(d.keys())
    #           super().__init__(d)
    #       def __setitem__(self, key, item):
    #           super().__setitem__(key, item)
    #           if key not in self._keys:
    #               self._keys.append(key)
    #   x = C(d={'foo':0})
    #   y = copy.deepcopy(x)
    #   self.assertEqual(x, y)
    #   self.assertEqual(x._keys, y._keys)
    #   self.assertIsNot(x, y)
    #   x['bar'] = 1
    #   self.assertNotEqual(x, y)
    #   self.assertNotEqual(x._keys, y._keys)

    # def test_copy_list_subclass(self):
    #   class C(list):
    #       pass
    #   x = C([[1, 2], 3])
    #   x.foo = [4, 5]
    #   y = copy.copy(x)
    #   self.assertEqual(list(x), list(y))
    #   self.assertEqual(x.foo, y.foo)
    #   self.assertIs(x[0], y[0])
    #   self.assertIs(x.foo, y.foo)

    # def test_deepcopy_list_subclass(self):
    #   class C(list):
    #       pass
    #   x = C([[1, 2], 3])
    #   x.foo = [4, 5]
    #   y = copy.deepcopy(x)
    #   self.assertEqual(list(x), list(y))
    #   self.assertEqual(x.foo, y.foo)
    #   self.assertIsNot(x[0], y[0])
    #   self.assertIsNot(x.foo, y.foo)

    # def test_copy_tuple_subclass(self):
    #   class C(tuple):
    #       pass
    #   x = C([1, 2, 3])
    #   # self.assertEqual(tus
    # def test_deepcopy_tuple_subclass(self):
#         class C(tuple):
#             pass
#         x = C([[1, 2], 3])
#         self.assertEqual(tuple(x), ([1, 2], 3))
#         y = copy.deepcopy(x)
#         self.assertEqual(tuple(y), ([1, 2], 3))
#         self.assertIsNot(x, y)
#         self.assertIsNot(x[0], y[0])

    # def test_getstate_exc(self):
    #   class EvilState(object):
    #       def __getstate__(self):
    #           raise ValueError("ain't got no stickin' state")
    #   self.assertRaises(ValueError, copy.copy, EvilState())

    def test_copy_function(self):
        self.assertEqual(copy.copy(global_foo), global_foo)
        def foo(x, y): return x+y
        self.assertEqual(copy.copy(foo), foo)
        bar = lambda: None
        self.assertEqual(copy.copy(bar), bar)

    def test_deepcopy_function(self):
        self.assertEqual(copy.deepcopy(global_foo), global_foo)
        def foo(x, y): return x+y
        self.assertEqual(copy.deepcopy(foo), foo)
        bar = lambda: None
        self.assertEqual(copy.deepcopy(bar), bar)

#     def _check_weakref(self, _copy):
#         class C(object):
#             pass
#         obj = C()
#         x = weakref.ref(obj)
#         y = _copy(x)
#         self.assertIs(y, x)
#         del obj
#         y = _copy(x)
#         self.assertIs(y, x)

#     def test_copy_weakref(self):
#         self._check_weakref(copy.copy)

#     def test_deepcopy_weakref(self):
#         self._check_weakref(copy.deepcopy)

    def _check_copy_weakdict(self, _dicttype):
        class C(object):
            pass
        a, b, c, d = [C() for i in range(4)]
        u = _dicttype()
        u[a] = b
        u[c] = d
        v = copy.copy(u)
        self.assertIsNot(v, u)
        self.assertEqual(v, u)
        self.assertEqual(v[a], b)
        self.assertEqual(v[c], d)
        self.assertEqual(len(v), 2)
        del c, d
        self.assertEqual(len(v), 1)
        x, y = C(), C()
        # The underlying containers are decoupled
        v[x] = y

        self.assertNotIn(x, u)

    # def test_copy_weakkeydict(self):
    #   self._check_copy_weakdict(weakref.WeakKeyDictionary)

    # def test_copy_weakvaluedict(self):
    #   self._check_copy_weakdict(weakref.WeakValueDictionary)

    # def test_deepcopy_weakkeydict(self):
    #   class C(object):
    #       def __init__(self, i):
    #           self.i = i
    #   a, b, c, d = [C(i) for i in range(4)]
    #   u = weakref.WeakKeyDictionary()
    #   u[a] = b
    #   u[c] = d
    #   # Keys aren't copied, values are
    #   v = copy.deepcopy(u)
    #   self.assertNotEqual(v, u)
    #   self.assertEqual(len(v), 2)
    #   self.assertIsNot(v[a], b)
    #   self.assertIsNot(v[c], d)
    #   self.assertEqual(v[a].i, b.i)
    #   self.assertEqual(v[c].i, d.i)
    #   del c
    #   self.assertEqual(len(v), 1)

#     def test_deepcopy_weakvaluedict(self):
#         class C(object):
#             def __init__(self, i):
#                 self.i = i
#         a, b, c, d = [C(i) for i in range(4)]
#         u = weakref.WeakValueDictionary()
#         u[a] = b
#         u[c] = d
#         # Keys are copied, values aren't
#         v = copy.deepcopy(u)
#         self.assertNotEqual(v, u)
#         self.assertEqual(len(v), 2)
#         (x, y), (z, t) = sorted(v.items(), key=lambda pair: pair[0].i)
#         self.assertIsNot(x, a)
#         self.assertEqual(x.i, a.i)
#         self.assertIs(y, b)
#         self.assertIsNot(z, c)
#         self.assertEqual(z.i, c.i)
#         self.assertIs(t, d)
#         del x, y, z, t
#         del d
#         self.assertEqual(len(v), 1)

    def test_deepcopy_methods(self):
        class potato:
            def cut(self):
                print "slices"
        mashed = potato()
        d1 = {"a": mashed.cut}
        d2 = copy.deepcopy(d1)
        # self.assertFalse(d1 == d2)
        # self.assertFalse(d1 is d2)
        # im = mashed.cut
        # im_dc = copy.deepcopy(im)
        # self.assertTrue(isinstance, (im, types.MethodType))
        # self.assertFalse(im == im_dc)
        # self.assertFalse(im is im_dc)

    def test_deepcopy_instance_methods(self):
        class potato:
            def cut(self):
                pass
        mashed = potato()
        d1 = {"mashed": mashed}
        d2 = copy.deepcopy(d1)
        d2["mashed"].cut()

def global_foo(x, y): return x+y

if __name__ == "__main__":
    unittest.main()