diff --git a/mapytex/calculus/API/tokens/number.py b/mapytex/calculus/API/tokens/number.py index 1c37949..b2cd9db 100644 --- a/mapytex/calculus/API/tokens/number.py +++ b/mapytex/calculus/API/tokens/number.py @@ -10,10 +10,12 @@ 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 -from decimal import Decimal as _Decimal __all__ = ["Integer", "Decimal"] @@ -51,17 +53,49 @@ class Integer(Token): return cls(mo, name, ancestor) @classmethod - def random(cls): - raise NotImplemented + 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 """ + """ 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, (str, float)): - mo = MOnumber(Decimal(a)) + if isinstance(a, _Decimal): + mo = MOnumber(a) + elif isinstance(a, (str, float)): + mo = MOnumber(_Decimal(a)) else: raise TypeError else: @@ -80,12 +114,43 @@ class Decimal(Token): return cls(mo, name, ancestor) @classmethod - def random(cls): - raise NotImplemented + 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 """ + """ Token representing a fraction + + :example: + >>> Fraction("3/4") + + """ def __init__(self, a, name="", ancestor=None): if not isinstance(a, MO): @@ -112,8 +177,62 @@ class Fraction(Token): return cls(mo, name, ancestor) @classmethod - def random(cls): - raise NotImplemented + 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):