#!/usr/bin/env python3
"""
Operator Interface

This module exports a set of functions corresponding to the intrinsic 
operators of Python.  For example, operator.add(x, y) is equivalent 
to the expression x+y.  The function names are those used for special 
methods; variants without leading and trailing '__' are also provided 
for convenience.

This is the pure Python implementation of the module.
"""

# downloaded from http://bugs.python.org/file28327/operator.py

#import builtins as _bi  #there is no builtins module

def lt(a, b):
    "Same as a < b."
    return a < b
__lt__ = lt

def le(a, b):
    "Same as a <= b."
    return a <= b
__le__ = le

def eq(a, b):
    "Same as a == b."
    return a == b
__eq__ = eq

def ne(a, b):
    "Same as a != b."
    return a != b
__ne__ = ne

def ge(a, b):
    "Same as a >= b."
    return a >= b
__ge__ = ge

def gt(a, b):
    "Same as a > b."
    return a > b
__gt__ = gt

def not_(a):
    "Same as not a."
    return not a
__not__ = not_

def truth(a):
    "Return True if a is true, False otherwise."
    #return _bi.bool(a)
    return bool(a)

def is_(a, b):
    "Same as a is b."
    return a is b

# brython does not like  (causes syntax error)
#def is_not(a, b):
#    "Same as a is not b."
#    return a is not b

#recursion error or just comment out and add code below function
#def abs(a):
#    "Same as abs(a)."
#    #return _bi.abs(a)
#    return abs(a)
__abs__ = abs
abs=abs


def add(a, b):
    "Same as a + b."
    return a + b
__add__ = add

def and_(a, b):
    "Same as a & b."
    return a & b
__and__ = and_

def floordiv(a, b):
    "Same as a // b."
    return a // b
__floordiv__ = floordiv

def index(a):
    "Same as a.__index__()."
    return a.__index__()
__index__ = index

def inv(a):
    "Same as ~a."
    return ~a    #brython does not like
    #return a^(2**31)
invert = __inv__ = __invert__ = inv

def lshift(a, b):
    "Same as a << b."
    return a << b
__lshift__ = lshift

def mod(a, b):
    "Same as a % b."
    return a % b
__mod__ = mod

def mul(a, b):
    "Same as a * b."
    return a * b
__mul__ = mul

def neg(a):
    "Same as -a."
    return -a
__neg__ = neg

def or_(a, b):
    "Same as a | b."
    return a | b
__or__ = or_

def pos(a):
    "Same as +a."
    return +a    #brython does not like
    if a >= 0: return a
    return -a
__pos__ = pos

def pow(a, b):
    "Same as a ** b."
    return a ** b
__pow__ = pow

def rshift(a, b):
    "Same as a >> b."
    return a >> b
__rshift__ = rshift

def sub(a, b):
    "Same as a - b."
    return a - b
__sub__ = sub

def truediv(a, b):
    "Same as a / b."
    return a / b
__truediv__ = truediv

def xor(a, b):
    "Same as a ^ b."
    return a ^ b
__xor__ = xor

def concat(a, b):
    "Same as a + b, for a and b sequences."
    if not (hasattr(a, '__getitem__') and hasattr(b, '__getitem__')):
        raise TypeError('a and b must be sequences')
    return a + b
__concat__ = concat

def contains(a, b):
    "Same as b in a (note reversed operands)."
    return b in a
__contains__ = contains

def countOf(a, b):
    "Return the number of times b occurs in a."
    count = 0
    for i in a:
        if i == b:
            count += 1
    return count

def delitem(a, b):
    "Same as del a[b]."
    del a[b]
__delitem__ = delitem

def getitem(a, b):
    "Same as a[b]."
    return a[b]
__getitem__ = getitem

#fixme  brython doesn't like this function
def indexOf(a, b):
    "Return the first index of b in a."
    #for i, j in _bi.enumerate(a):
    for i, j in enumerate(a):
        if j == b:
            return i
    else:
        raise ValueError('b not found in a')

def setitem(a, b, c):
    "Same as a[b] = c."
    a[b] = c
__setitem__ = setitem



class attrgetter:
    """
    Return a callable object that fetches the given attribute(s) from its operand.
    After f=attrgetter('name'), the call f(r) returns r.name.
    After g=attrgetter('name', 'date'), the call g(r) returns (r.name, r.date).
    After h=attrgetter('name.first', 'name.last'), the call h(r) returns
    (r.name.first, r.name.last).
    """
    def __init__(self, attr, *attrs):
        self._attrs = (attr,)
        self._attrs += attrs
        if any(not isinstance(attr, str) for attr in self._attrs):
            raise TypeError('attribute name must be a string')

    @staticmethod
    def _resolve_attr(obj, attr):
        for name in attr.split('.'):
            #obj = _bi.getattr(obj, name)
            obj = getattr(obj, name)
        return obj

    def __call__(self, obj):
        if len(self._attrs) == 1:
            return self._resolve_attr(obj, self._attrs[0])
        return tuple(self._resolve_attr(obj, attr) for attr in self._attrs)

class itemgetter:
    """
    Return a callable object that fetches the given item(s) from its operand.
    After f=itemgetter(2), the call f(r) returns r[2].
    After g=itemgetter(2,5,3), the call g(r) returns (r[2], r[5], r[3])
    """
    def __init__(self, item, *items):
        self._items = (item,)
        self._items += items

    def __call__(self, obj):
        if len(self._items) == 1:
            return obj[self._items[0]]
        return tuple(obj[item] for item in self._items)

class methodcaller:
    """
    Return a callable object that calls the given method on its operand.
    After f = methodcaller('name'), the call f(r) returns r.name().
    After g = methodcaller('name', 'date', foo=1), the call g(r) returns
    r.name('date', foo=1).
    """

    def __init__(self, name, *args, **kwargs):
        self._name = name
        self._args = args
        self._kwargs = kwargs

    def __call__(self, obj):
        return getattr(obj, self._name)(*self._args, **self._kwargs)


def iadd(a, b):
    "Same as a += b."
    a += b
    return a
__iadd__ = iadd

def iand(a, b):
    "Same as a &= b."
    a &= b
    return a
__iand__ = iand

def iconcat(a, b):
    "Same as a += b, for a and b sequences."
    if not (hasattr(a, '__getitem__') and hasattr(b, '__getitem__')):
        raise TypeError('a and b must be sequences')
    a += b
    return a
__iconcat__ = iconcat

def ifloordiv(a, b):
    "Same as a //= b."
    a //= b
    return a
__ifloordiv__ = ifloordiv

def ilshift(a, b):
    "Same as a <<= b."
    a <<= b
    return a
__ilshift__ = ilshift

def imod(a, b):
    "Same as a %= b."
    a %= b
    return a
__imod__ = imod

def imul(a, b):
    "Same as a *= b."
    a *= b
    return a
__imul__ = imul

def ior(a, b):
    "Same as a |= b."
    a |= b
    return a
__ior__ = ior

def ipow(a, b):
    "Same as a **= b."
    a **=b
    return a
__ipow__ = ipow

def irshift(a, b):
    "Same as a >>= b."
    a >>= b
    return a
__irshift__ = irshift

def isub(a, b):
    "Same as a -= b."
    a -= b
    return a
__isub__ = isub

def itruediv(a, b):
    "Same as a /= b."
    a /= b
    return a
__itruediv__ = itruediv

def ixor(a, b):
    "Same as a ^= b."
    a ^= b
    return a
__ixor__ = ixor

def length_hint(obj, default=0):
    """
    Return an estimate of the number of items in obj.
    This is useful for presizing containers when building from an iterable.

    If the object supports len(), the result will be exact. Otherwise, it may
    over- or under-estimate by an arbitrary amount. The result will be an
    integer >= 0.
    """
    try:
        return len(obj)
    except TypeError:
        try:
            val = obj.__length_hint__()
            if val is NotImplemented:
                raise TypeError
        except (AttributeError, TypeError):
            return default
        else:
            if not val > 0:
                raise ValueError('default must be > 0')
            return val

#try:
#    from _operator import *
#    from _operator import __doc__
#except ImportError:
#   pass
