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)
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]
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)) ]
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
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
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)
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)