#! /usr/bin/env python # -*- coding: utf-8 -*- # vim:fenc=utf-8 # # Copyright © 2017 lafrite # # Distributed under terms of the MIT license. """ Tokens representing interger and decimal """ from decimal import Decimal as _Decimal from random import random, randint from .token import Token from ...core.arithmetic import gcd from ...core.MO import MO, MOnumber from ...core.MO.fraction import MOFraction __all__ = ["Integer", "Decimal"] class Integer(Token): """ Token representing a integer :example: >>> Integer(4) >>> a = MOnumber(4) >>> Integer.from_mo(a) """ def __init__(self, a, name="", ancestor=None): if not isinstance(a, MO): if not isinstance(a, int): raise TypeError mo = MOnumber(a) else: mo = a Token.__init__(self, mo, name, ancestor) self._mathtype = 'entier' @classmethod def from_mo(cls, mo, name="", ancestor=None): if not isinstance(mo, MOnumber): raise TypeError if not isinstance(mo.value, int): raise TypeError return cls(mo, name, ancestor) @classmethod def random(cls, name = "", min_value = -10, max_value = 10, rejected = [0, 1], reject_callbacks=[], ): """ Generate a random Integer :param name: name of the Integer :param min_value: minimum value :param max_value: maximum value :param rejected: rejected values :param reject_callbacks: list of function for value rejection """ conditions = [lambda x: x in rejected] + reject_callbacks candidate = randint(min_value, max_value) while any(c(candidate) for c in conditions): candidate = randint(min_value, max_value) return Integer(candidate, name) class Decimal(Token): """ Token representing a decimal :example: >>> Decimal("4.3") >>> Decimal(3.3) >>> Decimal(_Decimal("2.3")) """ def __init__(self, a, name="", ancestor=None): if not isinstance(a, MO): if isinstance(a, _Decimal): mo = MOnumber(a) elif isinstance(a, (str, float)): mo = MOnumber(_Decimal(a)) else: raise TypeError else: mo = a self._mathtype = 'décimal' Token.__init__(self, mo, name, ancestor) @classmethod def from_mo(cls, mo, name="", ancestor=None): if not isinstance(mo, MOnumber): raise TypeError if not isinstance(mo.value, _Decimal): raise TypeError return cls(mo, name, ancestor) @classmethod def random(cls, name= "", min_value = -10, max_value = 10, digits = 2, rejected = [0, 1], reject_callbacks=[], ): """ Generate a random Decimal :param name: name of the Integer :param min_value: minimum value :param max_value: maximum value :param digits: digits after comas :param rejected: rejected values :param reject_callbacks: list of function for value rejection """ conditions = [lambda x: x in rejected] + reject_callbacks float_cand = (max_value - min_value)*random() + min_value candidate = _Decimal(f"{float_cand:.{digits}f}") while any(c(candidate) for c in conditions): float_cand = (max_value - min_value)*random() + min_value candidate = _Decimal(f"{float_cand:.{digits}f}") return Decimal(candidate, name) class Fraction(Token): """ Token representing a fraction :example: >>> Fraction("3/4") """ def __init__(self, a, name="", ancestor=None): if not isinstance(a, MO): if isinstance(a, str): num, denom = a.split('/') mo = MOFraction(int(num), int(denom)) else: raise TypeError else: mo = a self._mathtype = 'fraction' Token.__init__(self, mo, name, ancestor) @classmethod def from_mo(cls, mo, name="", ancestor=None): if not isinstance(mo, MOFraction): raise TypeError if not isinstance(mo._numerator, MOnumber): raise TypeError if not isinstance(mo._denominator, MOnumber): raise TypeError return cls(mo, name, ancestor) @classmethod def random(cls, name="", fix_num="", min_num=-10, max_num=10, rejected_num=[0], reject_num_callbacks=[], fix_denom="", min_denom=-10, max_denom=10, rejected_denom=[0, 1, -1], reject_denom_callbacks=[], irreductible=False, not_integer=True ): """ Generate a random Fraction :param name: Name of the fraction :param fix_num: if set, the numerator will get this value :param min_num: minimum value for the numerator :param max_num: maximum value for the numerator :param rejected_num: rejected values for the numerator :param reject_num_callbacks: list of function for numerator rejection :param fix_denom: if set, the denomerator will get this value :param min_denom: minimum value for the denominator :param max_denom: maximum value for the denominator :param rejected_denom: rejected values for the denominator :param reject_denom_callbacks: list of function for denomerator rejection :param irreductible: is the generated fraction necessary irreductible :param not_integer: can the generated fraction be egal to an interger """ if fix_num == "": conditions = [lambda x: x in rejected_denom] + reject_num_callbacks num = randint(min_num, max_num) while any(c(num) for c in conditions): num = randint(min_num, max_num) else: num = fix_num if fix_denom == "": conditions = [lambda x: x in rejected_denom] + reject_denom_callbacks if irreductible: def not_prime_with_num(denom): return gcd(num, denom) != 1 conditions.append(not_prime_with_num) if not_integer: def divise_num(denom): return num % denom == 0 conditions.append(divise_num) denom = randint(min_denom, max_denom) while any(c(denom) for c in conditions) : denom = randint(min_denom, max_denom) else: denom = fix_denom frac = MOFraction(num, denom) return cls(frac, name) @property def numerator(self): return self._mo.numerator @property def denominator(self): return self._mo.denominator # ----------------------------- # Reglages pour 'vim' # vim:set autoindent expandtab tabstop=4 shiftwidth=4: # cursor: 16 del