Example #1
0
def move_fracdigits_to(n, from_nb=None):
    """
    Turn n into decimal instead of all decimals found in the from_nb list.

    Each decimal found in the numbers' list will be recursively replaced by
    10 times itself (until it is no decimal anymore) while in the same time
    n will be divided by 10.

    This is useful for the case division by a decimal is unwanted.

    :param n: the number who will be divided by 10 instead of the others
    :type n: any number (int, Decimal, float though they're not advised)
    :param from_nb: an iterable containing the numbers that must be integers
    :type from_nb: a list (of numbers)
    :rtype: a list (of numbers)
    """
    if type(from_nb) is not list:
        raise TypeError('A list of numbers must be given as argument '
                        '\'numbers\'.')
    if not is_number(n):
        raise TypeError('The first argument must be a number.')
    n = Decimal(str(n))
    # If any element of from_nb is not a number, is_integer() will raise
    # an exception.
    if all([is_integer(i) for i in from_nb]):
        return [
            n,
        ] + [i for i in from_nb]
    numbers_copy = copy.deepcopy(from_nb)
    for i, j in enumerate(from_nb):
        if not is_integer(j):
            numbers_copy[i] = j * 10
            return move_fracdigits_to(n / 10, from_nb=numbers_copy)
Example #2
0
def remove_fracdigits_from(number, to=None):
    """
    Turn a number of the to list into a decimal, instead of number.

    In some cases this is not possible (for instance when all numbers of the
    to list are multiples of 10), then a ValueError is raised.

    :param number: the number that must be turned into integer
    :type number: decimal.Decimal
    :param to: the list of numbers where to find an integer that must be
               turned into a decimal
    :type to: list
    :rtype: a list (of numbers)
    """
    if not isinstance(number, Decimal):
        raise TypeError('The first argument must be a Decimal number.')
    if is_integer(number):
        raise TypeError('The first argument must be a decimal number.')
    if type(to) is not list:
        raise TypeError('Argument to: must be a list.')
    n = Number(number).fracdigits_nb()
    try:
        i = to.index(next(x for x in to if not is_integer(x / 10**n)))
    except StopIteration:
        raise ValueError('None of the numbers of to can be turned into '
                         'decimal.')
    to[i] = Number(to[i]) / Number(10)**n
    return [
        Number(number * 10**n).standardized(),
    ] + [x for x in to]
Example #3
0
    def dividing_points(self, n=None, prefix='a'):
        """
        Create the list of Points that divide the Bipoint in n parts.

        :param n: the number of parts (so it will create n - 1 points)
        n must be greater or equal to 1
        :type n: int
        """
        if not (is_number(n) and is_integer(n)):
            raise TypeError('n must be an integer')
        if not n >= 1:
            raise ValueError('n must be greater or equal to 1')
        x0 = self.points[0].x
        x1 = self.points[1].x
        xstep = (x1 - x0) / n
        x_list = [x0 + (i + 1) * xstep for i in range(int(n - 1))]
        y0 = self.points[0].y
        y1 = self.points[1].y
        ystep = (y1 - y0) / n
        y_list = [y0 + (i + 1) * ystep for i in range(int(n - 1))]
        if self.three_dimensional:
            z0 = self.points[0].z
            z1 = self.points[1].z
            zstep = (z1 - z0) / n
            z_list = [z0 + (i + 1) * zstep for i in range(int(n - 1))]
        else:
            z_list = ['undefined' for i in range(int(n - 1))]
        return [
            Point(x, y, z, prefix + str(i + 1))
            for i, (x, y, z) in enumerate(zip(x_list, y_list, z_list))
        ]
Example #4
0
 def __new__(cls, hour=0, minute=0, second=0, context=None):
     if isinstance(hour, ClockTime):
         if context is None:
             context = hour.context
         hour, minute, second = hour.hour, hour.minute, hour.second
     if any(not is_integer(value) for value in [hour, minute, second]):
         raise TypeError('hour, minute and second must be <class \'int\'>. '
                         'Found {}, {} and {} instead.'.format(
                             type(hour), type(minute), type(second)))
     if second < 0:
         minute -= abs(second) // 60 + 1
     if second >= 60:
         minute += second // 60
     second = second % 60
     if minute < 0:
         hour -= abs(minute) // 60 + 1
     if minute >= 60:
         hour += minute // 60
     minute = minute % 60
     hour = hour % 24
     self = super().__new__(cls)
     self._hour, self._minute, self._second = hour, minute, second
     self._context = deepcopy(config.clocktime.CONTEXT)
     if context is not None:
         check_clocktime_context(context)
         self._context.update(context)
     return self
Example #5
0
 def __new__(cls,
             sign=None,
             numerator=None,
             denominator=None,
             from_decimal=None):
     """
     Fraction(5, 8) is equivalent to Fraction('+', 5, 8).
     """
     if from_decimal is not None:
         f = Number(10)**max(1, from_decimal.fracdigits_nb())
         sign = Sign(from_decimal)
         numerator = (f * from_decimal).standardized()
         denominator = f.standardized()
     elif sign in ['+', '-']:
         if not (is_number(numerator) and is_number(denominator)):
             raise TypeError('Numerator and denominator must be numbers. '
                             'Got {} and {} instead.'.format(
                                 type(numerator), type(denominator)))
         if not (is_integer(numerator) and is_integer(denominator)):
             raise TypeError('Numerator and denominator must be integers. '
                             'Got {} and {} instead.'.format(
                                 numerator, denominator))
     else:
         if not (is_number(sign) and is_number(numerator)):
             raise TypeError('Numerator and denominator must be numbers. '
                             'Got {} and {} instead.'.format(
                                 type(sign), type(numerator)))
         elif not (is_integer(sign) and is_integer(numerator)):
             raise TypeError('Numerator and denominator must be integers. '
                             'Got {} and {} instead.'.format(
                                 sign, numerator))
         denominator = numerator
         numerator = sign
         sign = '+'
     # some initialization
     self = object.__new__(cls)
     self._sign = Sign(sign)
     self._numerator = Number(numerator)
     self._denominator = Number(denominator)
     return self
Example #6
0
 def reduced_by(self, n):
     """Return Fraction reduced by n (possibly return a Number)."""
     if not is_number(n):
         raise TypeError('A Fraction can be reduced only by an integer, '
                         'got {} instead.'.format(type(n)))
     if not is_integer(n):
         raise TypeError('A Fraction can be reduced only by an integer, '
                         'got {} instead.'.format(n))
     if not is_integer(self.numerator / n):
         raise ValueError(
             'Cannot divide {} by {} and get an integer.'.format(
                 self.numerator, n))
     if not is_integer(self.denominator / n):
         raise ValueError(
             'Cannot divide {} by {} and get an integer.'.format(
                 self.denominator, n))
     n = abs(n)
     if self.denominator / n == 1:
         return self.sign * self.numerator / n
     if self.denominator / n == -1:
         return Sign('-') * self.sign * self.numerator / n
     return Fraction(self.sign, self.numerator / n, self.denominator / n)
Example #7
0
    def split(self,
              operation='sum',
              dig=0,
              return_all=False,
              int_as_halves=False,
              int_as_quarters=False,
              int_as_halves_or_quarters=False,
              integer_split_at_unit=False):
        """
        Split self as a sum or difference, e.g. self = a + b or self = a - b

        By default, a and b have as many fractional digits as n does.
        The 'dig' keyword tells how many extra digits must have a and b
        (compared to self). For instance, if n=Decimal('4.5'), operation='sum',
        dig=1, then self will be split into 2-fractional-digits numbers,
        like 2.14 + 2.36.

        :param operation: must be 'sum', 'difference', '+' or '-'
        :type operation: str
        :param dig: extra depth level to use
        :type dig: int
        :param int_as_halves: whether integers should be split as two integers
        ± 0.5. Disabled if self is not integer.
        :type int_as_halves: bool
        :param int_as_quarters: whether integers should be split as two
        integers ± 0.25 Disabled if self is not integer.
        :type int_as_quarters: bool
        :param int_as_halves_or_quarters: whether integers should be split as
        two integers ± 0.5 or ±0.25 (randomly). Disabled if self is not
        integer.
        :type int_as_halves_or_quarters: bool
        :rtype: tuple (of numbers)
        """
        if operation not in ['sum', 'difference', '+', '-']:
            raise ValueError('Argument "operation" should be either \'sum\' '
                             'or \'difference\'.')
        if integer_split_at_unit:
            n_depth = self.fracdigits_nb()
            depth = dig + self.fracdigits_nb()
        else:
            n_depth = self.lowest_nonzero_digit_index()
            depth = dig + self.lowest_nonzero_digit_index()
        n = self
        if operation in ['sum', '+']:
            if self.is_power_of_10() and abs(self) <= 1 and dig == 0:
                # This case is impossible: write 1 as a sum of two natural
                # numbers bigger than 1, or 0.1 as a sum of two positive
                # decimals having 1 digit either, etc. so we arbitrarily
                # replace self by a random number between 2 and 9
                warnings.warn('mathmakerlib is asked something impossible '
                              '(split {} as a sum of two numbers having as '
                              'many digits)'.format(self))
                n = random.choice([i + 2 for i in range(7)])
                n = n * (10**(Decimal(-n_depth)))
            amplitude = n
        elif operation in ['difference', '-']:
            amplitude = max(10**(n_depth), n)
        start, end = 0, int((amplitude) * 10**depth - 1)
        if start > end:
            start, end = end + 1, -1
        # default: all numbers, including integers
        seq = [(Decimal(i) + 1) / Decimal(10)**Decimal(depth)
               for i in range(start, end)]
        # then if decimals are wanted, we remove the results that do not match
        # the wanted "depth" (if depth == 2, we remove 0.4 for instance)
        if depth >= 1:
            seq = [i for i in seq if not is_integer(i * (10**(depth - 1)))]
        delta = 0
        if is_integer(self):
            if int_as_halves_or_quarters:
                h, q = (True, False) if random.choice([True, False]) \
                    else (False, True)
                int_as_halves, int_as_quarters = h, q
            if int_as_halves:
                delta = Number('0.5')
            if int_as_quarters:
                delta = Number('0.25')
        if return_all:
            if operation in ['sum', '+']:
                return [(Number(a) + delta, Number(n - a) - delta)
                        for a in seq]
            elif operation in ['difference', '-']:
                return [(Number(n + b) + delta, Number(b) + delta)
                        for b in seq]
        else:
            if operation in ['sum', '+']:
                a = random.choice(seq)
                b = n - a
                a += delta
                b -= delta
            elif operation in ['difference', '-']:
                b = random.choice(seq)
                a = n + b
                a += delta
                b += delta
            return (Number(a), Number(b))
 def fill(self, value):
     if not (is_integer(value) and 1 <= value <= self.n):
         raise TypeError('fill must be an 1 <= integer <= self.n == {}, '
                         'got {} instead.'
                         .format(self.n, value))
     self._fill = Number(value)
 def n(self, n):
     if not (is_number(n) and is_integer(n) and n >= 1):
         raise TypeError('n must be an integer >= 1, got {} instead.'
                         .format(n))
     self._n = Number(n)