def f(rv): if not (rv.is_Mul or rv.is_Pow and rv.base.func in (cos, sin) and (rv.exp.is_integer or rv.base.is_positive)): return rv if first: n, d = [expand_mul(i) for i in rv.as_numer_denom()] newn = TR8(n, first=False) newd = TR8(d, first=False) if newn != n or newd != d: rv = gcd_terms(newn / newd) if rv.is_Mul and rv.args[0].is_Rational and \ len(rv.args) == 2 and rv.args[1].is_Add: rv = Mul(*rv.as_coeff_Mul()) return rv args = {cos: [], sin: [], None: []} for a in ordered(Mul.make_args(rv)): if a.func in (cos, sin): args[a.func].append(a.args[0]) elif (a.is_Pow and a.exp.is_Integer and a.exp > 0 and \ a.base.func in (cos, sin)): # XXX this is ok but pathological expression could be handled # more efficiently as in TRmorrie args[a.base.func].extend([a.base.args[0]] * a.exp) else: args[None].append(a) c = args[cos] s = args[sin] if not (c and s or len(c) > 1 or len(s) > 1): return rv args = args[None] n = min(len(c), len(s)) for i in range(n): a1 = s.pop() a2 = c.pop() args.append((sin(a1 + a2) + sin(a1 - a2)) / 2) while len(c) > 1: a1 = c.pop() a2 = c.pop() args.append((cos(a1 + a2) + cos(a1 - a2)) / 2) if c: args.append(cos(c.pop())) while len(s) > 1: a1 = s.pop() a2 = s.pop() args.append((-cos(a1 + a2) + cos(a1 - a2)) / 2) if s: args.append(sin(s.pop())) return TR8(expand_mul(Mul(*args)))
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.is_zero: raise ZeroDivisionError("Modulo by zero") if p.is_finite is False or q.is_finite is False or p is nan or q is nan: return nan if p is 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, 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? # 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 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))