예제 #1
0
 def __neg__(self, context=None):
     amount = Decimal.__neg__(self)
     return self.__class__(amount)
예제 #2
0
class FVal(object):
    """A value to represent numbers for financial applications. At the moment
    we use the python Decimal library but the abstraction will help us change the
    underlying implementation if needed.

    At the moment we do not allow any operations against floating points. Even though
    floating points could be converted to Decimals before each operation we will
    use this restriction to make sure floating point numbers are rooted from the codebase first.
    """

    __slots__ = ('num',)

    def __init__(self, data):
        try:
            if isinstance(data, float):
                self.num = Decimal(str(data))
            elif isinstance(data, bytes):
                # assume it's an ascii string and try to decode the bytes to one
                self.num = Decimal(data.decode())
            elif isinstance(data, (Decimal, int, str)):
                self.num = Decimal(data)
            elif isinstance(data, FVal):
                self.num = data.num
            else:
                self.num = None

        except InvalidOperation:
            self.num = None

            if not self.num:
                raise ValueError(
                    'Expected string, int, float, or Decimal to initialize an FVal.'
                    'Found {}.'.format(type(data))
                )

    def __str__(self):
        return str(self.num)

    def __repr__(self):
        return 'FVal({})'.format(str(self.num))

    def __gt__(self, other):
        other = evaluate_input(other)
        return self.num.compare_signal(other) == Decimal('1')

    def __lt__(self, other):
        other = evaluate_input(other)
        return self.num.compare_signal(other) == Decimal('-1')

    def __le__(self, other):
        other = evaluate_input(other)
        return self.num.compare_signal(other) in (Decimal('-1'), Decimal('0'))

    def __ge__(self, other):
        other = evaluate_input(other)
        return self.num.compare_signal(other) in (Decimal('1'), Decimal('0'))

    def __eq__(self, other):
        other = evaluate_input(other)
        return self.num.compare_signal(other) == Decimal('0')

    def __add__(self, other):
        other = evaluate_input(other)
        return FVal(self.num.__add__(other))

    def __sub__(self, other):
        other = evaluate_input(other)
        return FVal(self.num.__sub__(other))

    def __mul__(self, other):
        other = evaluate_input(other)
        return FVal(self.num.__mul__(other))

    def __truediv__(self, other):
        other = evaluate_input(other)
        return FVal(self.num.__truediv__(other))

    def __floordiv__(self, other):
        other = evaluate_input(other)
        return FVal(self.num.__floordiv__(other))

    def __pow__(self, other):
        other = evaluate_input(other)
        return FVal(self.num.__pow__(other))

    def __radd__(self, other):
        other = evaluate_input(other)
        return FVal(self.num.__radd__(other))

    def __rsub__(self, other):
        other = evaluate_input(other)
        return FVal(self.num.__rsub__(other))

    def __rmul__(self, other):
        other = evaluate_input(other)
        return FVal(self.num.__rmul__(other))

    def __rtruediv__(self, other):
        other = evaluate_input(other)
        return FVal(self.num.__rtruediv__(other))

    def __rfloordiv__(self, other):
        other = evaluate_input(other)
        return FVal(self.num.__rfloordiv__(other))

    def __float__(self):
        return float(self.num)

    # --- Unary operands

    def __neg__(self):
        return FVal(self.num.__neg__())

    def __abs__(self):
        return FVal(self.num.copy_abs())

    # --- Other oparations

    def fma(self, other, third):
        """
        Fused multiply-add. Return self*other+third with no rounding of the
        intermediate product self*other
        """
        other = evaluate_input(other)
        third = evaluate_input(third)
        return FVal(self.num.fma(other, third))

    def to_percentage(self):
        return '{:.5%}'.format(self.num)

    def to_int(self, exact):
        """Tries to convert to int, If `exact` is true then it will convert only if
        it is a whole decimal number; i.e.: if it has got nothing after the decimal point"""
        if exact and self.num.to_integral_exact() != self.num:
            raise ValueError('Tried to ask for exact int from {}'.format(self.num))
        return int(self.num)

    def is_close(self, other, max_diff="1e-6"):
        max_diff = FVal(max_diff)

        if not isinstance(other, FVal):
            other = FVal(other)

        diff_num = abs(self.num - other.num)
        return diff_num <= max_diff.num
예제 #3
0
class FVal():
    """A value to represent numbers for financial applications. At the moment
    we use the python Decimal library but the abstraction will help us change the
    underlying implementation if needed.

    At the moment we do not allow any operations against floating points. Even though
    floating points could be converted to Decimals before each operation we will
    use this restriction to make sure floating point numbers are rooted from the codebase first.
    """

    __slots__ = ('num',)

    def __init__(self, data: AcceptableFValInitInput = 0):

        try:
            if isinstance(data, float):
                self.num = Decimal(str(data))
            elif isinstance(data, bytes):
                # assume it's an ascii string and try to decode the bytes to one
                self.num = Decimal(data.decode())
            elif isinstance(data, bool):  # type: ignore
                # This elif has to come before the isinstance(int) check due to
                # https://stackoverflow.com/questions/37888620/comparing-boolean-and-int-using-isinstance
                raise ValueError('Invalid type bool for data given to FVal constructor')
            elif isinstance(data, (Decimal, int, str)):
                self.num = Decimal(data)
            elif isinstance(data, FVal):
                self.num = data.num
            else:
                raise ValueError(f'Invalid type {type(data)} of data given to FVal constructor')

        except InvalidOperation as e:
            raise ValueError(
                'Expected string, int, float, or Decimal to initialize an FVal.'
                'Found {}.'.format(type(data)),
            ) from e

    def __str__(self) -> str:
        return str(self.num)

    def __repr__(self) -> str:
        return 'FVal({})'.format(str(self.num))

    def __gt__(self, other: AcceptableFValOtherInput) -> bool:
        evaluated_other = evaluate_input(other)
        return self.num.compare_signal(evaluated_other) == Decimal('1')

    def __lt__(self, other: AcceptableFValOtherInput) -> bool:
        evaluated_other = evaluate_input(other)
        return self.num.compare_signal(evaluated_other) == Decimal('-1')

    def __le__(self, other: AcceptableFValOtherInput) -> bool:
        evaluated_other = evaluate_input(other)
        return self.num.compare_signal(evaluated_other) in (Decimal('-1'), Decimal('0'))

    def __ge__(self, other: AcceptableFValOtherInput) -> bool:
        evaluated_other = evaluate_input(other)
        return self.num.compare_signal(evaluated_other) in (Decimal('1'), Decimal('0'))

    def __eq__(self, other: object) -> bool:
        evaluated_other = evaluate_input(other)
        return self.num.compare_signal(evaluated_other) == Decimal('0')

    def __add__(self, other: AcceptableFValOtherInput) -> 'FVal':
        evaluated_other = evaluate_input(other)
        return FVal(self.num.__add__(evaluated_other))

    def __sub__(self, other: AcceptableFValOtherInput) -> 'FVal':
        evaluated_other = evaluate_input(other)
        return FVal(self.num.__sub__(evaluated_other))

    def __mul__(self, other: AcceptableFValOtherInput) -> 'FVal':
        evaluated_other = evaluate_input(other)
        return FVal(self.num.__mul__(evaluated_other))

    def __truediv__(self, other: AcceptableFValOtherInput) -> 'FVal':
        evaluated_other = evaluate_input(other)
        return FVal(self.num.__truediv__(evaluated_other))

    def __floordiv__(self, other: AcceptableFValOtherInput) -> 'FVal':
        evaluated_other = evaluate_input(other)
        return FVal(self.num.__floordiv__(evaluated_other))

    def __pow__(self, other: AcceptableFValOtherInput) -> 'FVal':
        evaluated_other = evaluate_input(other)
        return FVal(self.num.__pow__(evaluated_other))

    def __radd__(self, other: AcceptableFValOtherInput) -> 'FVal':
        evaluated_other = evaluate_input(other)
        return FVal(self.num.__radd__(evaluated_other))

    def __rsub__(self, other: AcceptableFValOtherInput) -> 'FVal':
        evaluated_other = evaluate_input(other)
        return FVal(self.num.__rsub__(evaluated_other))

    def __rmul__(self, other: AcceptableFValOtherInput) -> 'FVal':
        evaluated_other = evaluate_input(other)
        return FVal(self.num.__rmul__(evaluated_other))

    def __rtruediv__(self, other: AcceptableFValOtherInput) -> 'FVal':
        evaluated_other = evaluate_input(other)
        return FVal(self.num.__rtruediv__(evaluated_other))

    def __rfloordiv__(self, other: AcceptableFValOtherInput) -> 'FVal':
        evaluated_other = evaluate_input(other)
        return FVal(self.num.__rfloordiv__(evaluated_other))

    def __mod__(self, other: AcceptableFValOtherInput) -> 'FVal':
        evaluated_other = evaluate_input(other)
        return FVal(self.num.__mod__(evaluated_other))

    def __rmod__(self, other: AcceptableFValOtherInput) -> 'FVal':
        evaluated_other = evaluate_input(other)
        return FVal(self.num.__rmod__(evaluated_other))

    def __float__(self) -> float:
        return float(self.num)

    # --- Unary operands

    def __neg__(self) -> 'FVal':
        return FVal(self.num.__neg__())

    def __abs__(self) -> 'FVal':
        return FVal(self.num.copy_abs())

    # --- Other operations

    def fma(self, other: AcceptableFValOtherInput, third: AcceptableFValOtherInput) -> 'FVal':
        """
        Fused multiply-add. Return self*other+third with no rounding of the
        intermediate product self*other
        """
        evaluated_other = evaluate_input(other)
        evaluated_third = evaluate_input(third)
        return FVal(self.num.fma(evaluated_other, evaluated_third))

    def to_percentage(self, precision: int = 4, with_perc_sign: bool = True) -> str:
        return f'{self.num*100:.{precision}f}{"%" if with_perc_sign else ""}'

    def to_int(self, exact: bool) -> int:
        """
        Tries to convert to int, If `exact` is true then it will convert only if
        it is a whole decimal number; i.e.: if it has got nothing after the decimal point

        Raises:
            ConversionError: If exact was True but the FVal is actually not an exact integer.
        """
        if exact and self.num.to_integral_exact() != self.num:
            raise ConversionError(f'Tried to ask for exact int from {self.num}')
        return int(self.num)

    def is_close(self, other: AcceptableFValInitInput, max_diff: str = "1e-6") -> bool:
        evaluated_max_diff = FVal(max_diff)

        if not isinstance(other, FVal):
            other = FVal(other)

        diff_num = abs(self.num - other.num)
        return diff_num <= evaluated_max_diff.num
예제 #4
0
 def __neg__(self, context=None):
     amount = Decimal.__neg__(self)
     return self.__class__(amount)
예제 #5
0
class ContractingDecimal:
    def _get_other(self, other):
        if type(other) == ContractingDecimal:
            return other._d
        elif type(other) == float or type(other) == int:
            o = str(other)
            return Decimal(neg_sci_not(o))
        return other

    def __init__(self, a):
        if type(a) == float or type(a) == int:
            o = str(a)
            self._d = Decimal(neg_sci_not(o))
        elif type(a) == Decimal:
            self._d = a
        elif type(a) == str:
            self._d = Decimal(neg_sci_not(a))
        else:
            self._d = Decimal(a)

    def __bool__(self):
        return self._d > 0

    def __eq__(self, other):
        return self._d.__eq__(self._get_other(other))

    def __lt__(self, other):
        return self._d.__lt__(self._get_other(other))

    def __le__(self, other):
        return self._d.__le__(self._get_other(other))

    def __gt__(self, other):
        return self._d.__gt__(self._get_other(other))

    def __ge__(self, other):
        return self._d.__ge__(self._get_other(other))

    def __str__(self):
        return self._d.__str__()

    def __neg__(self):
        return self._d.__neg__()

    def __pos__(self):
        return self._d.__pos__()

    def __abs__(self):
        return self._d.__abs__()

    def __add__(self, other):
        x = self._d.__add__(self._get_other(other))
        return fix_precision(x)

    def __radd__(self, other):
        return fix_precision(self._d.__radd__(self._get_other(other)))

    def __sub__(self, other):
        return fix_precision(self._d.__sub__(self._get_other(other)))

    def __rsub__(self, other):
        return fix_precision(self._d.__rsub__(self._get_other(other)))

    def __mul__(self, other):
        return fix_precision(self._d.__mul__(self._get_other(other)))

    def __rmul__(self, other):
        return fix_precision(self._d.__rmul__(self._get_other(other)))

    def __truediv__(self, other):
        return fix_precision(self._d.__truediv__(self._get_other(other)))

    def __rtruediv__(self, other):
        return fix_precision(self._d.__rtruediv__(self._get_other(other)))

    def __divmod__(self, other):
        return fix_precision(self._d.__divmod__(self._get_other(other)))

    def __rdivmod__(self, other):
        return fix_precision(self._d.__divmod__(self._get_other(other)))

    def __mod__(self, other):
        return fix_precision(self._d.__mod__(self._get_other(other)))

    def __rmod__(self, other):
        return fix_precision(self._d.__rmod__(self._get_other(other)))

    def __floordiv__(self, other):
        return fix_precision(self._d.__floordiv__(self._get_other(other)))

    def __rfloordiv__(self, other):
        return fix_precision(self._d.__rfloordiv__(self._get_other(other)))

    def __pow__(self, other):
        return fix_precision(self._d.__pow__(self._get_other(other)))

    def __rpow__(self, other):
        return fix_precision(self._d.__rpow__(self._get_other(other)))

    def __int__(self):
        return self._d.__int__()

    def __float__(self):
        return float(self._d)

    def __round__(self, n=None):
        return self._d.__round__(n)
예제 #6
0
def generate_coupon_item_total_amount(coupon_items):
    total_amount = 0.00
    for item in coupon_items:
        total_amount = Decimal(total_amount) + Decimal(item['amount_net'])
    return total_amount.__neg__()
예제 #7
0
class Decibel(object):
    @classmethod
    def from_factor(cls, gain_factor, context=pcontext):
        """Create a Decibel object give the gain factor.
        >>> Decibel.from_factor(1)
        Decibel('0.000000')
        >>> Decibel.from_factor(3)
        Decibel('4.771213')
        """
        return cls(mwatt2db(gain_factor))

    @classmethod
    def from_10ths_db(cls, inval):
        """
        >>> dB = Decibel.from_10ths_db(22)
        >>> str(dB)
        '2.20'
        >>> dB = Decibel.from_10ths_db(None)
        >>> str(dB)
        'None'
        """
        if inval is None or inval == "None":
            dB = None
        else:
            dB = Decibel(inval).dB / D(10)
        return cls(dB)

    @classmethod
    def from_100ths_db(cls, inval):
        """
        >>> dB = Decibel.from_100ths_db(202)
        >>> str(dB)
        '2.02'
        >>> dB = Decibel.from_10ths_db(None)
        >>> str(dB)
        'None'
        """
        if inval is None or inval == "None":
            dB = None
        else:
            dB = D(inval) / D(100)
        return cls(dB)

    def __init__(self, dB):
        if dB is None or (is_str(dB) and dB == "None"):
            self.dB = None
        else:
            try:
                dB = dB.replace('dB', '')
            except AttributeError:
                pass
            if isinstance(dB, Decibel):
                self.dB = dB.dB
            else:
                self.dB = Decimal(dB).quantize(SIXPLACES, rounding=ROUND_HALF_EVEN)

    def __repr__(self):
        """
        >>> Decibel(1)
        Decibel('1.000000')
        >>> Decibel(.01)
        Decibel('0.010000')
        >>> Decibel(None)
        Decibel(None)
        >>> Decibel("None")
        Decibel(None)
        """
        if self.dB is None:
            return "Decibel(None)"
        return "Decibel('{}')".format(self.dB)

    def __hash__(self):
        return self.dB.__hash__()

    def __float__(self):
        return float(self.dB)

    def __format__(self, format_spec):  # pylint: disable=W0221
        """
        >>> str("{:+2.2f}".format(Decibal(100)))
        '+100.00'
        >>> str("{:+2.2f}".format(Decibal(0)))
        '+0.00'
        >>> str("{:+2.2f}".format(Decibal(None)))
        'None'
        """
        if self.dB is None:
            # XXX really want to format this.
            return "None"
        return self.dB.__format__(format_spec)

    def __str__(self):  # pylint: disable=W0222
        """
        >>> str(Decibel(0))
        '0.00'
        >>> str(Decibel(3.141596))
        '3.14'
        >>> str(Decibel(-3.999))
        '-4.00'
        >>> str(Decibel(None))
        'None'
        """
        if self.dB is None:
            return "None"
        else:
            return "{:.2f}".format(self.dB)

    def __abs__(self):
        if self.dB is None:
            return Decibel(None)
        elif self.dB < 0:
            return Decibel(self.dB.__neg__())
        else:
            return Decibel(self.dB)

    def __neg__(self):  # pylint: disable=W0222
        """
        >>> str(-Decibel(1))
        '-1.00'
        >>> str(-Decibel(-1))
        '1.00'
        """
        if self.dB is None:
            return Decibel(None)
        return Decibel(self.dB.__neg__())

    def __add__(self, add_atten):  # pylint: disable=W0221
        """
        >>> Decibel(1) + Decibel(3)
        Decibel('4.000000')
        >>> Decibel(-1) + 3
        Decibel('2.000000')
        """
        if hasattr(add_atten, "dB"):
            return Decibel(self.dB + add_atten.dB)
        else:
            return Decibel(self.dB + add_atten)

    def __sub__(self, sub_atten):  # pylint: disable=W0221
        """
        >>> Decibel(1) - Decibel(3)
        Decibel('-2.000000')
        >>> Decibel(2) - 1
        Decibel('1.000000')
        """
        if hasattr(sub_atten, "dB"):
            return Decibel(self.dB - sub_atten.dB)
        else:
            return Decibel(self.dB - sub_atten)

    def __mul__(self, other):  # pylint: disable=W0222
        """
        >>> Decibel(.5) * 20
        Decibel('10.000000')
        """
        if self.dB is None or other is None:
            return Decibel(None)
        if hasattr(other, "dB") and other.dB is None:
            return Decibel(None)
        return Decibel(self.dB * other)

    __rmul__ = __mul__

    def __lt__(self, other):
        """
        >>> Gain(0) > Gain(.1)
        False
        >>> Gain(.1) > Gain(0)
        True
        >>> Gain(0) < Gain(.1)
        True
        >>> Gain(.1) < Gain(0)
        False
        >>> Gain(0) == Gain(0)
        True
        >>> Gain(.1) == Gain(.1)
        True
        >>> Gain(0) != Gain(None)
        True
        >>> Gain(0) == Gain(None)
        False
        >>> Gain(None) == Gain(None)
        True
        >>> Gain(None) >= Gain(-50)
        False
        >>> Gain(None) < Gain(-50)
        True
        >>> Gain(None) <= Gain(-50)
        True
        """
        if self.dB is None:
            if other is None or other == "None":
                return False
            if hasattr(other, "dB") and other.dB is None:
                return False
            # other is not None so it's greater
            return True
        elif other is None or other == "None" or (hasattr(other, "dB") and other.dB is None):
            # self is not None other is so False
            return False
        return float(self.dB) < float(other)

    def __eq__(self, other):
        if self.dB is None:
            if other is None or other == "None":
                return True
            if hasattr(other, "dB") and other.dB is None:
                return True
            return False
        elif other is None or other == "None" or (hasattr(other, "dB") and other.dB is None):
            return self.dB is None
        return float(self.dB) == float(other)

    def gain_factor(self):
        """Convert gain value to mwatt for multiplying
        >>> Decibel(0).gain_factor()
        Decimal('1.000000')
        >>> Decibel(13).gain_factor()
        Decimal('19.952623')
        """
        #
        # Gain: x dB = 10 * log10(multiplier)
        # 10 ^ (x dB / 10) = 10^(log10(multiplier))
        # 10 ^ (x dB / 10) = multiplier
        #
        rv = Decimal(10 ** (self.dB / 10), pcontext)
        rv = rv.quantize(SIXPLACES, rounding=ROUND_HALF_EVEN)
        return rv

    mwatt_ratio = gain_factor

    def ase(self, nf, for_osnr=False):
        """Calculate ASE generated by amplification

        >>> Gain(23).ase(5.5)
        Power('-3.461412')
        """
        # Pase = hv(G -1) * NF * B0
        if not for_osnr:
            B0 = D('5.00e+12')  # all c-band (for .5nm)
        else:
            B0 = D('1.25e+10')  # @0.1nm resolution
        v = D('1.93e+14')  # speed of light
        h = D('6.63e-34')  # planck's constant
        nf = db2mwatt(nf)  # convert noise figure from dB to mwatts
        ase_watts = D(nf) * B0 * h * v * (self.mwatt_ratio() - 1)
        return Power.from_mwatt(ase_watts * 1000)

    def osnr(self, nf, powerin):
        """Calculate OSNR for .1nm signal

        >>> Gain(16).osnr(7.9, Power(-20))
        Decibel('30.170675')
        """

        # Psig = Pin * G
        # Pase = hv(G - 1) * NF * B0.1nm
        # OSNR = Psig / Pase
        psig = powerin + self
        pase = self.ase(nf, True)
        return psig - pase
예제 #8
0
class ContractingDecimal:
    def _get_other(self, other):
        if type(other) == ContractingDecimal:
            return other._d
        elif type(other) == float or type(other) == int:
            return Decimal(str(other))
        return other

    def __init__(self, a):
        if type(a) == float or type(a) == int:
            a = str(a)

        self._d = Decimal(a)

    def __bool__(self):
        return self._d > 0

    def __eq__(self, other):
        return self._d.__eq__(self._get_other(other))

    def __lt__(self, other):
        return self._d.__lt__(self._get_other(other))

    def __le__(self, other):
        return self._d.__le__(self._get_other(other))

    def __gt__(self, other):
        return self._d.__gt__(self._get_other(other))

    def __ge__(self, other):
        return self._d.__ge__(self._get_other(other))

    def __str__(self):
        return self._d.__str__()

    def __neg__(self):
        return self._d.__neg__()

    def __pos__(self):
        return self._d.__pos__()

    def __abs__(self):
        return self._d.__abs__()

    def __add__(self, other):
        x = self._d.__add__(self._get_other(other))
        return fix_precision(x)

    def __radd__(self, other):
        return fix_precision(self._d.__radd__(self._get_other(other)))

    def __sub__(self, other):
        return fix_precision(self._d.__sub__(self._get_other(other)))

    def __rsub__(self, other):
        return fix_precision(self._d.__rsub__(self._get_other(other)))

    def __mul__(self, other):
        return fix_precision(self._d.__mul__(self._get_other(other)))

    def __rmul__(self, other):
        return fix_precision(self._d.__rmul__(self._get_other(other)))

    def __truediv__(self, other):
        return fix_precision(self._d.__truediv__(self._get_other(other)))

    def __rtruediv__(self, other):
        return fix_precision(self._d.__rtruediv__(self._get_other(other)))

    def __divmod__(self, other):
        return fix_precision(self._d.__divmod__(self._get_other(other)))

    def __rdivmod__(self, other):
        return fix_precision(self._d.__divmod__(self._get_other(other)))

    def __mod__(self, other):
        return fix_precision(self._d.__mod__(self._get_other(other)))

    def __rmod__(self, other):
        return fix_precision(self._d.__rmod__(self._get_other(other)))

    def __floordiv__(self, other):
        return fix_precision(self._d.__floordiv__(self._get_other(other)))

    def __rfloordiv__(self, other):
        return fix_precision(self._d.__rfloordiv__(self._get_other(other)))

    def __pow__(self, other):
        return fix_precision(self._d.__pow__(self._get_other(other)))

    def __rpow__(self, other):
        return fix_precision(self._d.__rpow__(self._get_other(other)))

    def __int__(self):
        return self._d.__int__()

    def __float__(self):
        raise Exception('Cannot cast Decimal to float.')

    def __repr__(self):
        return self._d.__repr__()