def order(self, strategy="relator_based"): """ Returns the order of the finitely presented group ``self``. It uses the coset enumeration with identity group as subgroup, i.e ``H=[]``. Examples ======== >>> from sympy.combinatorics.free_groups import free_group >>> from sympy.combinatorics.fp_groups import FpGroup >>> F, x, y = free_group("x, y") >>> f = FpGroup(F, [x, y**2]) >>> f.order(strategy="coset_table_based") 2 """ from sympy.polys.polytools import gcd if self._order is not None: return self._order if self._coset_table is not None: self._order = len(self._coset_table.table) elif len(self.relators) == 0: self._order = self.free_group.order() elif len(self.generators) == 1: self._order = abs(gcd([r.array_form[0][1] for r in self.relators])) elif self._is_infinite(): self._order = S.Infinity else: gens, C = self._finite_index_subgroup() if C: ind = len(C.table) self._order = ind * self.subgroup(gens, C=C).order() else: self._order = self.index([]) return self._order
def _normalize(list_of_coeff, x, negative=True): """ Normalize a given annihilator """ lcm_denom = S(1) for i in list_of_coeff: lcm_denom = lcm(lcm_denom, i.as_numer_denom()[1]) if negative is True: lcm_denom = -lcm_denom for i, j in enumerate(list_of_coeff): list_of_coeff[i] = (j * lcm_denom).simplify() gcd_numer = list_of_coeff[-1] for i in list_of_coeff: gcd_numer = gcd(gcd_numer, i.as_numer_denom()[0]) for i, j in enumerate(list_of_coeff): list_of_coeff[i] = (j / gcd_numer).simplify() return list_of_coeff
def _simplification_technique_1(rels): """ All relators are checked to see if they are of the form `gen^n`. If any such relators are found then all other relators are processed for strings in the `gen` known order. Examples ======== >>> from sympy.combinatorics import free_group >>> from sympy.combinatorics.fp_groups import _simplification_technique_1 >>> F, x, y = free_group("x, y") >>> w1 = [x**2*y**4, x**3] >>> _simplification_technique_1(w1) [x**-1*y**4, x**3] >>> w2 = [x**2*y**-4*x**5, x**3, x**2*y**8, y**5] >>> _simplification_technique_1(w2) [x**-1*y*x**-1, x**3, x**-1*y**-2, y**5] >>> w3 = [x**6*y**4, x**4] >>> _simplification_technique_1(w3) [x**2*y**4, x**4] """ rels = rels[:] # dictionary with "gen: n" where gen^n is one of the relators exps = {} for i in range(len(rels)): rel = rels[i] if rel.number_syllables() == 1: g = rel[0] exp = abs(rel.array_form[0][1]) if rel.array_form[0][1] < 0: rels[i] = rels[i]**-1 g = g**-1 if g in exps: exp = gcd(exp, exps[g].array_form[0][1]) exps[g] = g**exp one_syllables_words = exps.values() # decrease some of the exponents in relators, making use of the single # syllable relators for i in range(len(rels)): rel = rels[i] if rel in one_syllables_words: continue rel = rel.eliminate_words(one_syllables_words, _all = True) # if rels[i] contains g**n where abs(n) is greater than half of the power p # of g in exps, g**n can be replaced by g**(n-p) (or g**(p-n) if n<0) for g in rel.contains_generators(): if g in exps: exp = exps[g].array_form[0][1] max_exp = (exp + 1)//2 rel = rel.eliminate_word(g**(max_exp), g**(max_exp-exp), _all = True) rel = rel.eliminate_word(g**(-max_exp), g**(-(max_exp-exp)), _all = True) rels[i] = rel rels = [r.identity_cyclic_reduction() for r in rels] return rels
def test_log_exact(): # check for pi/2, pi/3, pi/4, pi/6, pi/8, pi/12; pi/5, pi/10: for n in range(-23, 24): if gcd(n, 24) != 1: assert log(exp(n * I * pi / 24).rewrite(sqrt)) == n * I * pi / 24 for n in range(-9, 10): assert log(exp(n * I * pi / 10).rewrite(sqrt)) == n * I * pi / 10 assert log(S.Half - I * sqrt(3) / 2) == -I * pi / 3 assert log(Rational(-1, 2) + I * sqrt(3) / 2) == I * pi * Rational(2, 3) assert log(-sqrt(2) / 2 - I * sqrt(2) / 2) == -I * pi * Rational(3, 4) assert log(-sqrt(3) / 2 - I * S.Half) == -I * pi * Rational(5, 6) assert log( Rational(-1, 4) + sqrt(5) / 4 - I * sqrt(sqrt(5) / 8 + Rational(5, 8))) == -I * pi * Rational(2, 5) assert log( sqrt(Rational(5, 8) - sqrt(5) / 8) + I * (Rational(1, 4) + sqrt(5) / 4)) == I * pi * Rational(3, 10) assert log(-sqrt(sqrt(2) / 4 + S.Half) + I * sqrt(S.Half - sqrt(2) / 4)) == I * pi * Rational(7, 8) assert log(-sqrt(6) / 4 - sqrt(2) / 4 + I * (-sqrt(6) / 4 + sqrt(2) / 4)) == -I * pi * Rational(11, 12) assert log(-1 + I * sqrt(3)) == log(2) + I * pi * Rational(2, 3) assert log(5 + 5 * I) == log(5 * sqrt(2)) + I * pi / 4 assert log(sqrt(-12)) == log(2 * sqrt(3)) + I * pi / 2 assert log(-sqrt(6) + sqrt(2) - I * sqrt(6) - I * sqrt(2)) == log(4) - I * pi * Rational(7, 12) assert log(-sqrt(6 - 3 * sqrt(2)) - I * sqrt(6 + 3 * sqrt(2))) == log( 2 * sqrt(3)) - I * pi * Rational(5, 8) assert log(1 + I * sqrt(2 - sqrt(2)) / sqrt(2 + sqrt(2))) == log( 2 / sqrt(sqrt(2) + 2)) + I * pi / 8 assert log(cos(pi * Rational(7, 12)) + I * sin(pi * Rational(7, 12))) == I * pi * Rational(7, 12) assert log(cos(pi * Rational(6, 5)) + I * sin(pi * Rational(6, 5))) == I * pi * Rational(-4, 5) assert log(5 * (1 + I) / sqrt(2)) == log(5) + I * pi / 4 assert log( sqrt(2) * (-sqrt(3) + 1 - sqrt(3) * I - I)) == log(4) - I * pi * Rational(7, 12) assert log( -sqrt(2) * (1 - I * sqrt(3))) == log(2 * sqrt(2)) + I * pi * Rational(2, 3) assert log( sqrt(3) * I * (-sqrt(6 - 3 * sqrt(2)) - I * sqrt(3 * sqrt(2) + 6))) == log(6) - I * pi / 8 zero = (1 + sqrt(2))**2 - 3 - 2 * sqrt(2) assert log(zero - I * sqrt(3)) == log(sqrt(3)) - I * pi / 2 assert unchanged(log, zero + I * zero) or log(zero + zero * I) is zoo # bail quickly if no obvious simplification is possible: assert unchanged(log, (sqrt(2) - 1 / sqrt(sqrt(3) + I))**1000) # beware of non-real coefficients assert unchanged(log, sqrt(2 - sqrt(5)) * (1 + I))
def rsa_private_key(p, q, e): r""" The RSA *private key* is the pair `(n,d)`, where `n` is a product of two primes and `d` is the inverse of `e` (mod `\phi(n)`). Examples ======== >>> from sympy.crypto.crypto import rsa_private_key >>> p, q, e = 3, 5, 7 >>> rsa_private_key(p, q, e) (15, 7) """ n = p*q phi = totient(n) if isprime(p) and isprime(q) and gcd(e, phi) == 1: return n, pow(e, phi - 1, phi) return False
def rsa_private_key(p, q, e): r""" The RSA *private key* is the pair `(n,d)`, where `n` is a product of two primes and `d` is the inverse of `e` (mod `\phi(n)`). Examples ======== >>> from sympy.crypto.crypto import rsa_private_key >>> p, q, e = 3, 5, 7 >>> rsa_private_key(p, q, e) (15, 7) """ n = p * q phi = totient(n) if isprime(p) and isprime(q) and gcd(e, phi) == 1: return n, pow(e, phi - 1, phi) return False
def rsa_public_key(p, q, e): r""" The RSA *public key* is the pair `(n,e)`, where `n` is a product of two primes and `e` is relatively prime (coprime) to the Euler totient `\phi(n)`. Examples ======== >>> from sympy.crypto.crypto import rsa_public_key >>> p, q, e = 3, 5, 7 >>> n, e = rsa_public_key(p, q, e) >>> n 15 >>> e 7 """ n = p*q phi = totient(n) if isprime(p) and isprime(q) and gcd(e, phi) == 1: return n, e return False
def rsa_public_key(p, q, e): r""" The RSA *public key* is the pair `(n,e)`, where `n` is a product of two primes and `e` is relatively prime to the Euler totient `\phi(n)`. Examples ======== >>> from sympy.crypto.crypto import rsa_public_key >>> p, q, e = 3, 5, 7 >>> n, e = rsa_public_key(p, q, e) >>> n 15 >>> e 7 """ n = p * q phi = totient(n) if isprime(p) and isprime(q) and gcd(e, phi) == 1: return n, e return False
def eval(cls, p, q): from sympy.core.add import Add from sympy.core.mul import Mul from sympy.core.singleton import S from sympy.core.exprtools import gcd_terms from sympy.polys.polytools import gcd def doit(p, q): """Try to return p % q if both are numbers or +/-p is known to be less than q. """ if p == q or p == -q or p.is_Pow and p.exp.is_Integer and p.base == q: return S.Zero if p.is_Number and q.is_Number: return (p % q) # by ratio r = p/q try: d = int(r) except TypeError: pass else: if type(d) is int: rv = p - d*q if (rv*q < 0) is True: rv += q return rv # by differencec d = p - q if d.is_negative: if q.is_negative: return d elif q.is_positive: return p rv = doit(p, q) if rv is not None: return rv # denest if p.func is cls: # easy qinner = p.args[1] if qinner == q: return p # XXX other possibilities? # extract gcd; any further simplification should be done by the user G = gcd(p, q) if G is not S.One: p, q = [ gcd_terms(i/G, clear=False, fraction=False) for i in (p, q)] pwas, qwas = p, q # simplify terms # (x + y + 2) % x -> Mod(y + 2, x) if p.is_Add: args = [] for i in p.args: a = cls(i, q) if a.count(cls) > i.count(cls): args.append(i) else: args.append(a) if args != list(p.args): p = Add(*args) else: # handle coefficients if they are not Rational # since those are not handled by factor_terms # e.g. Mod(.6*x, .3*y) -> 0.3*Mod(2*x, y) cp, p = p.as_coeff_Mul() cq, q = q.as_coeff_Mul() ok = False if not cp.is_Rational or not cq.is_Rational: r = cp % cq if r == 0: G *= cq p *= int(cp/cq) ok = True if not ok: p = cp*p q = cq*q # simple -1 extraction if p.could_extract_minus_sign() and q.could_extract_minus_sign(): G, p, q = [-i for i in (G, p, q)] # check again to see if p and q can now be handled as numbers rv = doit(p, q) if rv is not None: return rv*G # put 1.0 from G on inside if G.is_Float and G == 1: p *= G return cls(p, q, evaluate=False) elif G.is_Mul and G.args[0].is_Float and G.args[0] == 1: p = G.args[0]*p G = Mul._from_args(G.args[1:]) return G*cls(p, q, evaluate=(p, q) != (pwas, qwas))
def denom_clean(l): return [(v / gcd(list(v))).applyfunc(simpfunc) for v in l]
def eval(cls, p, q): def doit(p, q): """Try to return p % q if both are numbers or +/-p is known to be less than or equal q. """ if q.is_zero: raise ZeroDivisionError("Modulo by zero") if p is S.NaN or q is S.NaN or p.is_finite is False or q.is_finite is False: return S.NaN if p is S.Zero or p in (q, -q) or (p.is_integer and q == 1): return S.Zero if q.is_Number: if p.is_Number: return p % q if q == 2: if p.is_even: return S.Zero elif p.is_odd: return S.One if hasattr(p, '_eval_Mod'): rv = getattr(p, '_eval_Mod')(q) if rv is not None: return rv # by ratio r = p / q if r.is_integer: return S.Zero try: d = int(r) except TypeError: pass else: if isinstance(d, int): rv = p - d * q if (rv * q < 0) == True: rv += q return rv # by difference # -2|q| < p < 2|q| d = abs(p) for _ in range(2): d -= abs(q) if d.is_negative: if q.is_positive: if p.is_positive: return d + q elif p.is_negative: return -d elif q.is_negative: if p.is_positive: return d elif p.is_negative: return -d + q break rv = doit(p, q) if rv is not None: return rv # denest if isinstance(p, cls): qinner = p.args[1] if qinner % q == 0: return cls(p.args[0], q) elif (qinner * (q - qinner)).is_nonnegative: # |qinner| < |q| and have same sign return p elif isinstance(-p, cls): qinner = (-p).args[1] if qinner % q == 0: return cls(-(-p).args[0], q) elif (qinner * (q + qinner)).is_nonpositive: # |qinner| < |q| and have different sign return p elif isinstance(p, Add): # separating into modulus and non modulus both_l = non_mod_l, mod_l = [], [] for arg in p.args: both_l[isinstance(arg, cls)].append(arg) # if q same for all if mod_l and all(inner.args[1] == q for inner in mod_l): net = Add(*non_mod_l) + Add(*[i.args[0] for i in mod_l]) return cls(net, q) elif isinstance(p, Mul): # separating into modulus and non modulus both_l = non_mod_l, mod_l = [], [] for arg in p.args: both_l[isinstance(arg, cls)].append(arg) if mod_l and all(inner.args[1] == q for inner in mod_l): # finding distributive term non_mod_l = [cls(x, q) for x in non_mod_l] mod = [] non_mod = [] for j in non_mod_l: if isinstance(j, cls): mod.append(j.args[0]) else: non_mod.append(j) prod_mod = Mul(*mod) prod_non_mod = Mul(*non_mod) prod_mod1 = Mul(*[i.args[0] for i in mod_l]) net = prod_mod1 * prod_mod return prod_non_mod * cls(net, q) if q.is_Integer and q is not S.One: _ = [] for i in non_mod_l: if i.is_Integer and (i % q is not S.Zero): _.append(i % q) else: _.append(i) non_mod_l = _ p = Mul(*(non_mod_l + mod_l)) # XXX other possibilities? from sympy.polys.polyerrors import PolynomialError from sympy.polys.polytools import gcd # extract gcd; any further simplification should be done by the user try: G = gcd(p, q) if G != 1: p, q = [ gcd_terms(i / G, clear=False, fraction=False) for i in (p, q) ] except PolynomialError: # issue 21373 G = S.One pwas, qwas = p, q # simplify terms # (x + y + 2) % x -> Mod(y + 2, x) if p.is_Add: args = [] for i in p.args: a = cls(i, q) if a.count(cls) > i.count(cls): args.append(i) else: args.append(a) if args != list(p.args): p = Add(*args) else: # handle coefficients if they are not Rational # since those are not handled by factor_terms # e.g. Mod(.6*x, .3*y) -> 0.3*Mod(2*x, y) cp, p = p.as_coeff_Mul() cq, q = q.as_coeff_Mul() ok = False if not cp.is_Rational or not cq.is_Rational: r = cp % cq if r == 0: G *= cq p *= int(cp / cq) ok = True if not ok: p = cp * p q = cq * q # simple -1 extraction if p.could_extract_minus_sign() and q.could_extract_minus_sign(): G, p, q = [-i for i in (G, p, q)] # check again to see if p and q can now be handled as numbers rv = doit(p, q) if rv is not None: return rv * G # put 1.0 from G on inside if G.is_Float and G == 1: p *= G return cls(p, q, evaluate=False) elif G.is_Mul and G.args[0].is_Float and G.args[0] == 1: p = G.args[0] * p G = Mul._from_args(G.args[1:]) return G * cls(p, q, evaluate=(p, q) != (pwas, qwas))
def _eval_simplify(self, **kwargs): from .add import Add from .expr import Expr r = self r = r.func(*[i.simplify(**kwargs) for i in r.args]) if r.is_Relational: if not isinstance(r.lhs, Expr) or not isinstance(r.rhs, Expr): return r dif = r.lhs - r.rhs # replace dif with a valid Number that will # allow a definitive comparison with 0 v = None if dif.is_comparable: v = dif.n(2) elif dif.equals(0): # XXX this is expensive v = S.Zero if v is not None: r = r.func._eval_relation(v, S.Zero) r = r.canonical # If there is only one symbol in the expression, # try to write it on a simplified form free = list( filter(lambda x: x.is_real is not False, r.free_symbols)) if len(free) == 1: try: from sympy.solvers.solveset import linear_coeffs x = free.pop() dif = r.lhs - r.rhs m, b = linear_coeffs(dif, x) if m.is_zero is False: if m.is_negative: # Dividing with a negative number, so change order of arguments # canonical will put the symbol back on the lhs later r = r.func(-b / m, x) else: r = r.func(x, -b / m) else: r = r.func(b, S.Zero) except ValueError: # maybe not a linear function, try polynomial from sympy.polys.polyerrors import PolynomialError from sympy.polys.polytools import gcd, Poly, poly try: p = poly(dif, x) c = p.all_coeffs() constant = c[-1] c[-1] = 0 scale = gcd(c) c = [ctmp / scale for ctmp in c] r = r.func( Poly.from_list(c, x).as_expr(), -constant / scale) except PolynomialError: pass elif len(free) >= 2: try: from sympy.solvers.solveset import linear_coeffs from sympy.polys.polytools import gcd free = list(ordered(free)) dif = r.lhs - r.rhs m = linear_coeffs(dif, *free) constant = m[-1] del m[-1] scale = gcd(m) m = [mtmp / scale for mtmp in m] nzm = list(filter(lambda f: f[0] != 0, list(zip(m, free)))) if scale.is_zero is False: if constant != 0: # lhs: expression, rhs: constant newexpr = Add(*[i * j for i, j in nzm]) r = r.func(newexpr, -constant / scale) else: # keep first term on lhs lhsterm = nzm[0][0] * nzm[0][1] del nzm[0] newexpr = Add(*[i * j for i, j in nzm]) r = r.func(lhsterm, -newexpr) else: r = r.func(constant, S.Zero) except ValueError: pass # Did we get a simplified result? r = r.canonical measure = kwargs['measure'] if measure(r) < kwargs['ratio'] * measure(self): return r else: return self
def eval(cls, p, q): from sympy.core.add import Add from sympy.core.mul import Mul from sympy.core.singleton import S from sympy.core.exprtools import gcd_terms from sympy.polys.polytools import gcd def doit(p, q): """Try to return p % q if both are numbers or +/-p is known to be less than or equal q. """ if q == S.Zero: raise ZeroDivisionError("Modulo by zero") if p.is_infinite or q.is_infinite or p is nan or q is nan: return nan if p == S.Zero or p == q or p == -q or (p.is_integer and q == 1): return S.Zero if q.is_Number: if p.is_Number: return p%q if q == 2: if p.is_even: return S.Zero elif p.is_odd: return S.One if hasattr(p, '_eval_Mod'): rv = getattr(p, '_eval_Mod')(q) if rv is not None: return rv # by ratio r = p/q try: d = int(r) except TypeError: pass else: if isinstance(d, integer_types): rv = p - d*q if (rv*q < 0) == True: rv += q return rv # by difference # -2|q| < p < 2|q| d = abs(p) for _ in range(2): d -= abs(q) if d.is_negative: if q.is_positive: if p.is_positive: return d + q elif p.is_negative: return -d elif q.is_negative: if p.is_positive: return d elif p.is_negative: return -d + q break rv = doit(p, q) if rv is not None: return rv # denest if isinstance(p, cls): qinner = p.args[1] if qinner % q == 0: return cls(p.args[0], q) elif (qinner*(q - qinner)).is_nonnegative: # |qinner| < |q| and have same sign return p elif isinstance(-p, cls): qinner = (-p).args[1] if qinner % q == 0: return cls(-(-p).args[0], q) elif (qinner*(q + qinner)).is_nonpositive: # |qinner| < |q| and have different sign return p elif isinstance(p, Add): # separating into modulus and non modulus both_l = non_mod_l, mod_l = [], [] for arg in p.args: both_l[isinstance(arg, cls)].append(arg) # if q same for all if mod_l and all(inner.args[1] == q for inner in mod_l): net = Add(*non_mod_l) + Add(*[i.args[0] for i in mod_l]) return cls(net, q) elif isinstance(p, Mul): # separating into modulus and non modulus both_l = non_mod_l, mod_l = [], [] for arg in p.args: both_l[isinstance(arg, cls)].append(arg) if mod_l and all(inner.args[1] == q for inner in mod_l): # finding distributive term non_mod_l = [cls(x, q) for x in non_mod_l] mod = [] non_mod = [] for j in non_mod_l: if isinstance(j, cls): mod.append(j.args[0]) else: non_mod.append(j) prod_mod = Mul(*mod) prod_non_mod = Mul(*non_mod) prod_mod1 = Mul(*[i.args[0] for i in mod_l]) net = prod_mod1*prod_mod return prod_non_mod*cls(net, q) if q.is_Integer and q is not S.One: _ = [] for i in non_mod_l: if i.is_Integer and (i % q is not S.Zero): _.append(i%q) else: _.append(i) non_mod_l = _ p = Mul(*(non_mod_l + mod_l)) # XXX other possibilities? # extract gcd; any further simplification should be done by the user G = gcd(p, q) if G != 1: p, q = [ gcd_terms(i/G, clear=False, fraction=False) for i in (p, q)] pwas, qwas = p, q # simplify terms # (x + y + 2) % x -> Mod(y + 2, x) if p.is_Add: args = [] for i in p.args: a = cls(i, q) if a.count(cls) > i.count(cls): args.append(i) else: args.append(a) if args != list(p.args): p = Add(*args) else: # handle coefficients if they are not Rational # since those are not handled by factor_terms # e.g. Mod(.6*x, .3*y) -> 0.3*Mod(2*x, y) cp, p = p.as_coeff_Mul() cq, q = q.as_coeff_Mul() ok = False if not cp.is_Rational or not cq.is_Rational: r = cp % cq if r == 0: G *= cq p *= int(cp/cq) ok = True if not ok: p = cp*p q = cq*q # simple -1 extraction if p.could_extract_minus_sign() and q.could_extract_minus_sign(): G, p, q = [-i for i in (G, p, q)] # check again to see if p and q can now be handled as numbers rv = doit(p, q) if rv is not None: return rv*G # put 1.0 from G on inside if G.is_Float and G == 1: p *= G return cls(p, q, evaluate=False) elif G.is_Mul and G.args[0].is_Float and G.args[0] == 1: p = G.args[0]*p G = Mul._from_args(G.args[1:]) return G*cls(p, q, evaluate=(p, q) != (pwas, qwas))
def denom_clean(l): from sympy.polys.polytools import gcd return [(v / gcd(list(v))).applyfunc(simpfunc) for v in l]
def eval(cls, p, q): from sympy.core.add import Add from sympy.core.mul import Mul from sympy.core.singleton import S from sympy.core.exprtools import gcd_terms from sympy.polys.polytools import gcd def doit(p, q): """Try to return p % q if both are numbers or +/-p is known to be less than or equal q. """ if q == S.Zero: raise ZeroDivisionError("Modulo by zero") if p.is_infinite or q.is_infinite or p is nan or q is nan: return nan if p == S.Zero or p == q or p == -q or (p.is_integer and q == 1): return S.Zero if q.is_Number: if p.is_Number: return (p % q) if q == 2: if p.is_even: return S.Zero elif p.is_odd: return S.One if hasattr(p, '_eval_Mod'): rv = getattr(p, '_eval_Mod')(q) if rv is not None: return rv # by ratio r = p / q try: d = int(r) except TypeError: pass else: if type(d) is int: rv = p - d * q if (rv * q < 0) == True: rv += q return rv # by difference # -2|q| < p < 2|q| d = abs(p) for _ in range(2): d -= abs(q) if d.is_negative: if q.is_positive: if p.is_positive: return d + q elif p.is_negative: return -d elif q.is_negative: if p.is_positive: return d elif p.is_negative: return -d + q break rv = doit(p, q) if rv is not None: return rv # denest if p.func is cls: qinner = p.args[1] if qinner % q == 0: return cls(p.args[0], q) elif (qinner * (q - qinner)).is_nonnegative: # |qinner| < |q| and have same sign return p elif (-p).func is cls: qinner = (-p).args[1] if qinner % q == 0: return cls(-(-p).args[0], q) elif (qinner * (q + qinner)).is_nonpositive: # |qinner| < |q| and have different sign return p # XXX other possibilities? # extract gcd; any further simplification should be done by the user G = gcd(p, q) if G != 1: p, q = [ gcd_terms(i / G, clear=False, fraction=False) for i in (p, q) ] pwas, qwas = p, q # simplify terms # (x + y + 2) % x -> Mod(y + 2, x) if p.is_Add: args = [] for i in p.args: a = cls(i, q) if a.count(cls) > i.count(cls): args.append(i) else: args.append(a) if args != list(p.args): p = Add(*args) else: # handle coefficients if they are not Rational # since those are not handled by factor_terms # e.g. Mod(.6*x, .3*y) -> 0.3*Mod(2*x, y) cp, p = p.as_coeff_Mul() cq, q = q.as_coeff_Mul() ok = False if not cp.is_Rational or not cq.is_Rational: r = cp % cq if r == 0: G *= cq p *= int(cp / cq) ok = True if not ok: p = cp * p q = cq * q # simple -1 extraction if p.could_extract_minus_sign() and q.could_extract_minus_sign(): G, p, q = [-i for i in (G, p, q)] # check again to see if p and q can now be handled as numbers rv = doit(p, q) if rv is not None: return rv * G # put 1.0 from G on inside if G.is_Float and G == 1: p *= G return cls(p, q, evaluate=False) elif G.is_Mul and G.args[0].is_Float and G.args[0] == 1: p = G.args[0] * p G = Mul._from_args(G.args[1:]) return G * cls(p, q, evaluate=(p, q) != (pwas, qwas))
def _regular_point_ellipse(self, a, b, c, d, e, f): D = 4*a*c - b**2 ok = D if not ok: raise ValueError("Rational Point on the conic does not exist") if a == 0 and c == 0: K = -1 L = 4*(d*e - b*f) elif c != 0: K = D L = 4*c**2*d**2 - 4*b*c*d*e + 4*a*c*e**2 + 4*b**2*c*f - 16*a*c**2*f else: K = D L = 4*a**2*e**2 - 4*b*a*d*e + 4*b**2*a*f ok = L != 0 and not(K > 0 and L < 0) if not ok: raise ValueError("Rational Point on the conic does not exist") K = Rational(K).limit_denominator(10**12) L = Rational(L).limit_denominator(10**12) k1, k2 = K.p, K.q l1, l2 = L.p, L.q g = gcd(k2, l2) a1 = (l2*k2)/g b1 = (k1*l2)/g c1 = -(l1*k2)/g a2 = sign(a1)*core(abs(a1), 2) r1 = sqrt(a1/a2) b2 = sign(b1)*core(abs(b1), 2) r2 = sqrt(b1/b2) c2 = sign(c1)*core(abs(c1), 2) r3 = sqrt(c1/c2) g = gcd(gcd(a2, b2), c2) a2 = a2/g b2 = b2/g c2 = c2/g g1 = gcd(a2, b2) a2 = a2/g1 b2 = b2/g1 c2 = c2*g1 g2 = gcd(a2,c2) a2 = a2/g2 b2 = b2*g2 c2 = c2/g2 g3 = gcd(b2, c2) a2 = a2*g3 b2 = b2/g3 c2 = c2/g3 x, y, z = symbols("x y z") eq = a2*x**2 + b2*y**2 + c2*z**2 solutions = diophantine(eq) if len(solutions) == 0: raise ValueError("Rational Point on the conic does not exist") flag = False for sol in solutions: syms = Tuple(*sol).free_symbols rep = {s: 3 for s in syms} sol_z = sol[2] if sol_z == 0: flag = True continue if not isinstance(sol_z, (int, Integer)): syms_z = sol_z.free_symbols if len(syms_z) == 1: p = next(iter(syms_z)) p_values = Complement(S.Integers, solveset(Eq(sol_z, 0), p, S.Integers)) rep[p] = next(iter(p_values)) if len(syms_z) == 2: p, q = list(ordered(syms_z)) for i in S.Integers: subs_sol_z = sol_z.subs(p, i) q_values = Complement(S.Integers, solveset(Eq(subs_sol_z, 0), q, S.Integers)) if not q_values.is_empty: rep[p] = i rep[q] = next(iter(q_values)) break if len(syms) != 0: x, y, z = tuple(s.subs(rep) for s in sol) else: x, y, z = sol flag = False break if flag: raise ValueError("Rational Point on the conic does not exist") x = (x*g3)/r1 y = (y*g2)/r2 z = (z*g1)/r3 x = x/z y = y/z if a == 0 and c == 0: x_reg = (x + y - 2*e)/(2*b) y_reg = (x - y - 2*d)/(2*b) elif c != 0: x_reg = (x - 2*d*c + b*e)/K y_reg = (y - b*x_reg - e)/(2*c) else: y_reg = (x - 2*e*a + b*d)/K x_reg = (y - b*y_reg - d)/(2*a) return x_reg, y_reg
def _ecm_one_factor(n, B1=10000, B2=100000, max_curve=200): """Returns one factor of n using Lenstra's 2 Stage Elliptic curve Factorization with Suyama's Parameterization. Here Montgomery arithmetic is used for fast computation of addition and doubling of points in elliptic curve. This ECM method considers elliptic curves in Montgomery form (E : b*y**2*z = x**3 + a*x**2*z + x*z**2) and involves elliptic curve operations (mod N), where the elements in Z are reduced (mod N). Since N is not a prime, E over FF(N) is not really an elliptic curve but we can still do point additions and doubling as if FF(N) was a field. Stage 1 : The basic algorithm involves taking a random point (P) on an elliptic curve in FF(N). The compute k*P using Montgomery ladder algorithm. Let q be an unknown factor of N. Then the order of the curve E, |E(FF(q))|, might be a smooth number that divides k. Then we have k = l * |E(FF(q))| for some l. For any point belonging to the curve E, |E(FF(q))|*P = O, hence k*P = l*|E(FF(q))|*P. Thus kP.z_cord = 0 (mod q), and the unknownn factor of N (q) can be recovered by taking gcd(kP.z_cord, N). Stage 2 : This is a continuation of Stage 1 if k*P != O. The idea utilize the fact that even if kP != 0, the value of k might miss just one large prime divisor of |E(FF(q))|. In this case we only need to compute the scalar multiplication by p to get p*k*P = O. Here a second bound B2 restrict the size of possible values of p. Parameters ========== n : Number to be Factored B1 : Stage 1 Bound B2 : Stage 2 Bound max_curve : Maximum number of curves generated References ========== .. [1] Carl Pomerance and Richard Crandall "Prime Numbers: A Computational Perspective" (2nd Ed.), page 344 """ n = as_int(n) if B1 % 2 != 0 or B2 % 2 != 0: raise ValueError("The Bounds should be an even integer") sieve.extend(B2) if isprime(n): return n from sympy.functions.elementary.miscellaneous import sqrt from sympy.polys.polytools import gcd curve = 0 D = int(sqrt(B2)) beta = [0] * (D + 1) S = [0] * (D + 1) k = 1 for p in sieve.primerange(1, B1 + 1): k *= pow(p, integer_log(B1, p)[0]) while (curve <= max_curve): curve += 1 #Suyama's Paramatrization sigma = rgen.randint(6, n - 1) u = (sigma * sigma - 5) % n v = (4 * sigma) % n diff = v - u u_3 = pow(u, 3, n) try: C = (pow(diff, 3, n) * (3 * u + v) * mod_inverse(4 * u_3 * v, n) - 2) % n except ValueError: #If the mod_inverse(4*u_3*v, n) doesn't exist return gcd(4 * u_3 * v, n) a24 = (C + 2) * mod_inverse(4, n) % n Q = Point(u_3, pow(v, 3, n), a24, n) Q = Q.mont_ladder(k) g = gcd(Q.z_cord, n) #Stage 1 factor if g != 1 and g != n: return g #Stage 1 failure. Q.z = 0, Try another curve elif g == n: continue #Stage 2 - Improved Standard Continuation S[1] = Q.double() S[2] = S[1].double() beta[1] = (S[1].x_cord * S[1].z_cord) % n beta[2] = (S[2].x_cord * S[2].z_cord) % n for d in range(3, D + 1): S[d] = S[d - 1].add(S[1], S[d - 2]) beta[d] = (S[d].x_cord * S[d].z_cord) % n g = 1 B = B1 - 1 T = Q.mont_ladder(B - 2 * D) R = Q.mont_ladder(B) for r in range(B, B2, 2 * D): alpha = (R.x_cord * R.z_cord) % n for q in sieve.primerange(r + 2, r + 2 * D + 1): delta = (q - r) // 2 f = (R.x_cord - S[d].x_cord)*(R.z_cord + S[d].z_cord) -\ alpha + beta[delta] g = (g * f) % n #Swap T, R = R, R.add(S[D], T) g = gcd(n, g) #Stage 2 Factor found if g != 1 and g != n: return g #ECM failed, Increase the bounds raise ValueError("Increase the bounds")