1"""compatibility layer for decimal (Python standard library)"""
2from __future__ import absolute_import
3from decimal import *
4
5
6try:
7    Decimal.from_float  # New in 2.7
8except AttributeError:
9    import math as _math
10
11    def _bit_length(integer):
12        s = bin(integer)    # binary representation:  bin(-37) --> '-0b100101'
13        s = s.lstrip('-0b') # remove leading zeros and minus sign
14        return len(s)       # len('100101') --> 6
15
16    @classmethod
17    def _from_float(cls, f):
18        if isinstance(f, int):                # handle integer inputs
19            return cls(f)
20        if not isinstance(f, float):
21            raise TypeError("argument must be int or float.")
22        if _math.isinf(f) or _math.isnan(f):
23            return cls(repr(f))
24        if _math.copysign(1.0, f) == 1.0:
25            sign = 0
26        else:
27            sign = 1
28        n, d = abs(f).as_integer_ratio()
29        #k = d.bit_length() - 1
30        k = _bit_length(d) - 1
31        result = _dec_from_triple(sign, str(n*5**k), -k)
32        if cls is Decimal:
33            return result
34        else:
35            return cls(result)
36
37    Decimal.from_float = _from_float
38
39
40if Decimal('1.0') != 1.0:  # Changed in Python 3.2
41
42    import numbers as _numbers
43    from decimal import _dec_from_triple
44
45
46    class FloatOperation(DecimalException, TypeError):
47        """Enable stricter semantics for mixing floats and Decimals."""
48        pass
49
50
51    # Adapted from Python 3.1 standard library.
52    _context_init_orig = Context.__init__
53    def _context_init_new(self, prec=None, rounding=None,
54                          traps=None, flags=None,
55                          Emin=None, Emax=None,
56                          capitals=None, _clamp=0,
57                          _ignored_flags=None):
58
59        # Call original __init__.
60        _context_init_orig(self, prec=prec, rounding=rounding, traps=traps,
61                           flags=flags, Emin=Emin, Emax=Emax, capitals=capitals,
62                           _clamp=_clamp, _ignored_flags=_ignored_flags)
63
64        # Add FloatOperation to `traps` dict.
65        self.traps[FloatOperation] = 0
66
67    Context.__init__ = _context_init_new
68
69
70    # Adapted from Python 3.4 standard library.
71    def _convert_for_comparison(self, other, equality_op=False):
72        if isinstance(other, Decimal):
73            return self, other
74        if isinstance(other, _numbers.Rational):
75            if not self._is_special:
76                self = _dec_from_triple(self._sign,
77                                        str(int(self._int) * other.denominator),
78                                        self._exp)
79            return self, Decimal(other.numerator)
80        if equality_op and isinstance(other, _numbers.Complex) and other.imag == 0:
81            other = other.real
82        if isinstance(other, float):
83            context = getcontext()
84            if equality_op:
85                context.flags[FloatOperation] = 1
86            else:
87                context._raise_error(FloatOperation,
88                    "strict semantics for mixing floats and Decimals are enabled")
89            return self, Decimal.from_float(other)
90        return NotImplemented, NotImplemented
91
92    def _eq(self, other, context=None):
93        self, other = _convert_for_comparison(self, other, equality_op=True)
94        if other is NotImplemented:
95            return other
96        if self._check_nans(other, context):
97            return False
98        return self._cmp(other) == 0
99    Decimal.__eq__ = _eq
100
101    def _ne(self, other, context=None):
102        self, other = _convert_for_comparison(self, other, equality_op=True)
103        if other is NotImplemented:
104            return other
105        if self._check_nans(other, context):
106            return True
107        return self._cmp(other) != 0
108    Decimal.__ne__ = _ne
109
110    def _lt(self, other, context=None):
111        self, other = _convert_for_comparison(self, other)
112        if other is NotImplemented:
113            return other
114        ans = self._compare_check_nans(other, context)
115        if ans:
116            return False
117        return self._cmp(other) < 0
118    Decimal.__lt__ = _lt
119
120    def _le(self, other, context=None):
121        self, other = _convert_for_comparison(self, other)
122        if other is NotImplemented:
123            return other
124        ans = self._compare_check_nans(other, context)
125        if ans:
126            return False
127        return self._cmp(other) <= 0
128    Decimal.__le__ = _le
129
130    def _gt(self, other, context=None):
131        self, other = _convert_for_comparison(self, other)
132        if other is NotImplemented:
133            return other
134        ans = self._compare_check_nans(other, context)
135        if ans:
136            return False
137        return self._cmp(other) > 0
138    Decimal.__gt__ = _gt
139
140    def _ge(self, other, context=None):
141        self, other = _convert_for_comparison(self, other)
142        if other is NotImplemented:
143            return other
144        ans = self._compare_check_nans(other, context)
145        if ans:
146            return False
147        return self._cmp(other) >= 0
148    Decimal.__ge__ = _ge
149