# Copyright 2007 Google, Inc. All Rights Reserved.

# Licensed to PSF under a Contributor Agreement.



"""Abstract Base Classes (ABCs) for collections, according to PEP 3119.



DON'T USE THIS MODULE DIRECTLY!  The classes here should be imported

via collections; they are defined here only to alleviate certain

bootstrapping issues.  Unit tests are in test_collections.

"""



from abc import ABCMeta, abstractmethod

import sys



__all__ = ["Hashable", "Iterable", "Iterator",

           "Sized", "Container", "Callable",

           "Set", "MutableSet",

           "Mapping", "MutableMapping",

           "MappingView", "KeysView", "ItemsView", "ValuesView",

           "Sequence", "MutableSequence",

           ]



### ONE-TRICK PONIES ###



class Hashable:

    __metaclass__ = ABCMeta



    @abstractmethod

    def __hash__(self):

        return 0



    @classmethod

    def __subclasshook__(cls, C):

        if cls is Hashable:

            for B in C.__mro__:

                if "__hash__" in B.__dict__:

                    if B.__dict__["__hash__"]:

                        return True

                    break

        return NotImplemented





class Iterable:

    __metaclass__ = ABCMeta



    @abstractmethod

    def __iter__(self):

        while False:

            yield None



    @classmethod

    def __subclasshook__(cls, C):

        if cls is Iterable:

            if any("__iter__" in B.__dict__ for B in C.__mro__):

                return True

        return NotImplemented



Iterable.register(str)





class Iterator(Iterable):



    @abstractmethod

    def next(self):

        raise StopIteration



    def __iter__(self):

        return self



    @classmethod

    def __subclasshook__(cls, C):

        if cls is Iterator:

            if any("next" in B.__dict__ for B in C.__mro__):

                return True

        return NotImplemented





class Sized:

    __metaclass__ = ABCMeta



    @abstractmethod

    def __len__(self):

        return 0



    @classmethod

    def __subclasshook__(cls, C):

        if cls is Sized:

            if any("__len__" in B.__dict__ for B in C.__mro__):

                return True

        return NotImplemented





class Container:

    __metaclass__ = ABCMeta



    @abstractmethod

    def __contains__(self, x):

        return False



    @classmethod

    def __subclasshook__(cls, C):

        if cls is Container:

            if any("__contains__" in B.__dict__ for B in C.__mro__):

                return True

        return NotImplemented





class Callable:

    __metaclass__ = ABCMeta



    @abstractmethod

    def __call__(self, *args, **kwds):

        return False



    @classmethod

    def __subclasshook__(cls, C):

        if cls is Callable:

            if any("__call__" in B.__dict__ for B in C.__mro__):

                return True

        return NotImplemented





### SETS ###





class Set(Sized, Iterable, Container):

    """A set is a finite, iterable container.



    This class provides concrete generic implementations of all

    methods except for __contains__, __iter__ and __len__.



    To override the comparisons (presumably for speed, as the

    semantics are fixed), all you have to do is redefine __le__ and

    then the other operations will automatically follow suit.

    """



    def __le__(self, other):

        if not isinstance(other, Set):

            return NotImplemented

        if len(self) > len(other):

            return False

        for elem in self:

            if elem not in other:

                return False

        return True



    def __lt__(self, other):

        if not isinstance(other, Set):

            return NotImplemented

        return len(self) < len(other) and self.__le__(other)



    def __gt__(self, other):

        if not isinstance(other, Set):

            return NotImplemented

        return other < self



    def __ge__(self, other):

        if not isinstance(other, Set):

            return NotImplemented

        return other <= self



    def __eq__(self, other):

        if not isinstance(other, Set):

            return NotImplemented

        return len(self) == len(other) and self.__le__(other)



    def __ne__(self, other):

        return not (self == other)



    @classmethod

    def _from_iterable(cls, it):

        '''Construct an instance of the class from any iterable input.



        Must override this method if the class constructor signature

        does not accept an iterable for an input.

        '''

        return cls(it)



    def __and__(self, other):

        if not isinstance(other, Iterable):

            return NotImplemented

        return self._from_iterable(value for value in other if value in self)



    def isdisjoint(self, other):

        for value in other:

            if value in self:

                return False

        return True



    def __or__(self, other):

        if not isinstance(other, Iterable):

            return NotImplemented

        chain = (e for s in (self, other) for e in s)

        return self._from_iterable(chain)



    def __sub__(self, other):

        if not isinstance(other, Set):

            if not isinstance(other, Iterable):

                return NotImplemented

            other = self._from_iterable(other)

        return self._from_iterable(value for value in self

                                   if value not in other)



    def __xor__(self, other):

        if not isinstance(other, Set):

            if not isinstance(other, Iterable):

                return NotImplemented

            other = self._from_iterable(other)

        return (self - other) | (other - self)



    # Sets are not hashable by default, but subclasses can change this

    __hash__ = None



    def _hash(self):

        """Compute the hash value of a set.



        Note that we don't define __hash__: not all sets are hashable.

        But if you define a hashable set type, its __hash__ should

        call this function.



        This must be compatible __eq__.



        All sets ought to compare equal if they contain the same

        elements, regardless of how they are implemented, and

        regardless of the order of the elements; so there's not much

        freedom for __eq__ or __hash__.  We match the algorithm used

        by the built-in frozenset type.

        """

        MAX = sys.maxint

        MASK = 2 * MAX + 1

        n = len(self)

        h = 1927868237 * (n + 1)

        h &= MASK

        for x in self:

            hx = hash(x)

            h ^= (hx ^ (hx << 16) ^ 89869747)  * 3644798167

            h &= MASK

        h = h * 69069 + 907133923

        h &= MASK

        if h > MAX:

            h -= MASK + 1

        if h == -1:

            h = 590923713

        return h



Set.register(frozenset)





class MutableSet(Set):



    @abstractmethod

    def add(self, value):

        """Add an element."""

        raise NotImplementedError



    @abstractmethod

    def discard(self, value):

        """Remove an element.  Do not raise an exception if absent."""

        raise NotImplementedError



    def remove(self, value):

        """Remove an element. If not a member, raise a KeyError."""

        if value not in self:

            raise KeyError(value)

        self.discard(value)



    def pop(self):

        """Return the popped value.  Raise KeyError if empty."""

        it = iter(self)

        try:

            value = next(it)

        except StopIteration:

            raise KeyError

        self.discard(value)

        return value



    def clear(self):

        """This is slow (creates N new iterators!) but effective."""

        try:

            while True:

                self.pop()

        except KeyError:

            pass



    def __ior__(self, it):

        for value in it:

            self.add(value)

        return self



    def __iand__(self, it):

        for value in (self - it):

            self.discard(value)

        return self



    def __ixor__(self, it):

        if not isinstance(it, Set):

            it = self._from_iterable(it)

        for value in it:

            if value in self:

                self.discard(value)

            else:

                self.add(value)

        return self



    def __isub__(self, it):

        for value in it:

            self.discard(value)

        return self



MutableSet.register(set)





### MAPPINGS ###





class Mapping(Sized, Iterable, Container):



    @abstractmethod

    def __getitem__(self, key):

        raise KeyError



    def get(self, key, default=None):

        try:

            return self[key]

        except KeyError:

            return default



    def __contains__(self, key):

        try:

            self[key]

        except KeyError:

            return False

        else:

            return True



    def iterkeys(self):

        return iter(self)



    def itervalues(self):

        for key in self:

            yield self[key]



    def iteritems(self):

        for key in self:

            yield (key, self[key])



    def keys(self):

        return list(self)



    def items(self):

        return [(key, self[key]) for key in self]



    def values(self):

        return [self[key] for key in self]



    # Mappings are not hashable by default, but subclasses can change this

    __hash__ = None



    def __eq__(self, other):

        return isinstance(other, Mapping) and \

               dict(self.items()) == dict(other.items())



    def __ne__(self, other):

        return not (self == other)



class MappingView(Sized):



    def __init__(self, mapping):

        self._mapping = mapping



    def __len__(self):

        return len(self._mapping)





class KeysView(MappingView, Set):



    def __contains__(self, key):

        return key in self._mapping



    def __iter__(self):

        for key in self._mapping:

            yield key





class ItemsView(MappingView, Set):



    def __contains__(self, item):

        key, value = item

        try:

            v = self._mapping[key]

        except KeyError:

            return False

        else:

            return v == value



    def __iter__(self):

        for key in self._mapping:

            yield (key, self._mapping[key])





class ValuesView(MappingView):



    def __contains__(self, value):

        for key in self._mapping:

            if value == self._mapping[key]:

                return True

        return False



    def __iter__(self):

        for key in self._mapping:

            yield self._mapping[key]





class MutableMapping(Mapping):



    @abstractmethod

    def __setitem__(self, key, value):

        raise KeyError



    @abstractmethod

    def __delitem__(self, key):

        raise KeyError



    __marker = object()



    def pop(self, key, default=__marker):

        try:

            value = self[key]

        except KeyError:

            if default is self.__marker:

                raise

            return default

        else:

            del self[key]

            return value



    def popitem(self):

        try:

            key = next(iter(self))

        except StopIteration:

            raise KeyError

        value = self[key]

        del self[key]

        return key, value



    def clear(self):

        try:

            while True:

                self.popitem()

        except KeyError:

            pass



    def update(self, other=(), **kwds):

        if isinstance(other, Mapping):

            for key in other:

                self[key] = other[key]

        elif hasattr(other, "keys"):

            for key in other.keys():

                self[key] = other[key]

        else:

            for key, value in other:

                self[key] = value

        for key, value in kwds.items():

            self[key] = value



    def setdefault(self, key, default=None):

        try:

            return self[key]

        except KeyError:

            self[key] = default

        return default



MutableMapping.register(dict)





### SEQUENCES ###





class Sequence(Sized, Iterable, Container):

    """All the operations on a read-only sequence.



    Concrete subclasses must override __new__ or __init__,

    __getitem__, and __len__.

    """



    @abstractmethod

    def __getitem__(self, index):

        raise IndexError



    def __iter__(self):

        i = 0

        try:

            while True:

                v = self[i]

                yield v

                i += 1

        except IndexError:

            return



    def __contains__(self, value):

        for v in self:

            if v == value:

                return True

        return False



    def __reversed__(self):

        for i in reversed(range(len(self))):

            yield self[i]



    def index(self, value):

        for i, v in enumerate(self):

            if v == value:

                return i

        raise ValueError



    def count(self, value):

        return sum(1 for v in self if v == value)



Sequence.register(tuple)

Sequence.register(basestring)

Sequence.register(buffer)

Sequence.register(xrange)





class MutableSequence(Sequence):



    @abstractmethod

    def __setitem__(self, index, value):

        raise IndexError



    @abstractmethod

    def __delitem__(self, index):

        raise IndexError



    @abstractmethod

    def insert(self, index, value):

        raise IndexError



    def append(self, value):

        self.insert(len(self), value)



    def reverse(self):

        n = len(self)

        for i in range(n//2):

            self[i], self[n-i-1] = self[n-i-1], self[i]



    def extend(self, values):

        for v in values:

            self.append(v)



    def pop(self, index=-1):

        v = self[index]

        del self[index]

        return v



    def remove(self, value):

        del self[self.index(value)]



    def __iadd__(self, values):

        self.extend(values)

        return self



MutableSequence.register(list)

