def __eq__(self, other): if not isinstance(other, Subs): return False if (len(self.point) != len(other.point) or self.free_symbols != other.free_symbols or sorted(self.point) != sorted(other.point)): return False # non-repeated point args selfargs = [ v[0] for v in sorted(zip(self.variables, self.point), key = lambda v: v[1]) if list(self.point.args).count(v[1]) == 1 ] otherargs = [ v[0] for v in sorted(zip(other.variables, other.point), key = lambda v: v[1]) if list(other.point.args).count(v[1]) == 1 ] # find repeated point values and subs each associated variable # for a single symbol selfrepargs = [] otherrepargs = [] if uniq(self.point) != self.point: repeated = uniq([ v for v in self.point if list(self.point.args).count(v) > 1 ]) repswap = dict(zip(repeated, [ C.Dummy() for _ in xrange(len(repeated)) ])) selfrepargs = [ (self.variables[i], repswap[v]) for i, v in enumerate(self.point) if v in repeated ] otherrepargs = [ (other.variables[i], repswap[v]) for i, v in enumerate(other.point) if v in repeated ] return self.expr.subs(selfrepargs) == other.expr.subs( tuple(zip(otherargs, selfargs))).subs(otherrepargs)
def _eval_extract(self, rowsList, colsList): urow = list(uniq(rowsList)) ucol = list(uniq(colsList)) smat = {} if len(urow)*len(ucol) < len(self._smat): # there are fewer elements requested than there are elements in the matrix for i, r in enumerate(urow): for j, c in enumerate(ucol): smat[i, j] = self._smat.get((r, c), 0) else: # most of the request will be zeros so check all of self's entries, # keeping only the ones that are desired for rk, ck in self._smat: if rk in urow and ck in ucol: smat[(urow.index(rk), ucol.index(ck))] = self._smat[(rk, ck)] rv = self._new(len(urow), len(ucol), smat) # rv is nominally correct but there might be rows/cols # which require duplication if len(rowsList) != len(urow): for i, r in enumerate(rowsList): i_previous = rowsList.index(r) if i_previous != i: rv = rv.row_insert(i, rv.row(i_previous)) if len(colsList) != len(ucol): for i, c in enumerate(colsList): i_previous = colsList.index(c) if i_previous != i: rv = rv.col_insert(i, rv.col(i_previous)) return rv
def _eval_extract(self, rowsList, colsList): urow = list(uniq(rowsList)) ucol = list(uniq(colsList)) smat = {} if len(urow) * len(ucol) < len(self._smat): # there are fewer elements requested than there are elements in the matrix for i, r in enumerate(urow): for j, c in enumerate(ucol): smat[i, j] = self._smat.get((r, c), 0) else: # most of the request will be zeros so check all of self's entries, # keeping only the ones that are desired for rk, ck in self._smat: if rk in urow and ck in ucol: smat[urow.index(rk), ucol.index(ck)] = self._smat[rk, ck] rv = self._new(len(urow), len(ucol), smat) # rv is nominally correct but there might be rows/cols # which require duplication if len(rowsList) != len(urow): for i, r in enumerate(rowsList): i_previous = rowsList.index(r) if i_previous != i: rv = rv.row_insert(i, rv.row(i_previous)) if len(colsList) != len(ucol): for i, c in enumerate(colsList): i_previous = colsList.index(c) if i_previous != i: rv = rv.col_insert(i, rv.col(i_previous)) return rv
def bifid6_square(key): r""" 6x6 Polybius square. Produces the Polybius square for the `6 \times 6` Bifid cipher. Assumes alphabet of symbols is "A", ..., "Z", "0", ..., "9". Examples ======== >>> from sympy.crypto.crypto import bifid6_square >>> key = "encrypt" >>> bifid6_square(key) Matrix([ [E, N, C, R, Y, P], [T, A, B, D, F, G], [H, I, J, K, L, M], [O, Q, S, U, V, W], [X, Z, 0, 1, 2, 3], [4, 5, 6, 7, 8, 9]]) """ A = alphabet_of_cipher() + [str(a) for a in range(10)] # first make sure the letters are capitalized # and text has no spaces key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] # create long key long_key = key0 + [x for x in A if not(x in key0)] f = lambda i, j: Symbol(long_key[6*i + j]) M = Matrix(6, 6, f) return M
def decipher_vigenere(ct, key, symbols="ABCDEFGHIJKLMNOPQRSTUVWXYZ"): """ Decode using the Vigenère cipher. Examples ======== >>> from sympy.crypto.crypto import decipher_vigenere >>> key = "encrypt" >>> ct = "QRGK kt HRZQE BPR" >>> decipher_vigenere(ct, key) 'MEETMEONMONDAY' """ symbols = "".join(symbols) A = alphabet_of_cipher(symbols) N = len(A) # normally, 26 key0 = uniq(key) key0 = [x.capitalize() for x in key0 if x.isalnum()] K = [A.index(x) for x in key0] k = len(K) ct0 = [x.capitalize() for x in ct if x.isalnum()] C = [A.index(x) for x in ct0] n = len(C) #m = n//k P = [(-K[i % k] + C[i]) % N for i in range(n)] return "".join([str(A[x]) for x in P])
def bifid7_square(key): r""" 7x7 Polybius square. Produce the Polybius square for the `7 \times 7` Bifid cipher. Assumes alphabet of symbols is "A", ..., "Z", "0", ..., "22". (Also, assumes you have some way of distinguishing "22" from "2", "2" juxtaposed together for deciphering...) Examples ======== >>> from sympy.crypto.crypto import bifid7_square >>> bifid7_square("gold bug") Matrix([ [ G, O, L, D, B, U, A], [ C, E, F, H, I, J, K], [ M, N, P, Q, R, S, T], [ V, W, X, Y, Z, 0, 1], [ 2, 3, 4, 5, 6, 7, 8], [ 9, 10, 11, 12, 13, 14, 15], [16, 17, 18, 19, 20, 21, 22]]) """ A = alphabet_of_cipher() + [str(a) for a in range(23)] # first make sure the letters are capitalized # and text has no spaces key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] # create long key long_key = key0 + [x for x in A if (not(x in key0))] f = lambda i, j: Symbol(long_key[7*i + j]) M = Matrix(7, 7, f) return M
def _run(coeffs): # find runs in coeffs such that the difference in terms (mod 1) # of t1, t2, ..., tn is 1/n u = list(uniq(coeffs)) for i in range(len(u)): dj = ([((u[j] - u[i]) % 1, j) for j in range(i + 1, len(u))]) for one, j in dj: if one.p == 1 and one.q != 1: n = one.q got = [i] get = list(range(1, n)) for d, j in dj: m = n*d if m.is_Integer and m in get: get.remove(m) got.append(j) if not get: break else: continue for i, j in enumerate(got): c = u[j] coeffs.remove(c) got[i] = c return one.q, got[0], got[1:]
def decipher_vigenere(ct, key, symbols="ABCDEFGHIJKLMNOPQRSTUVWXYZ"): """ Decode using the Vigenere cipher. Examples ======== >>> from sympy.crypto.crypto import decipher_vigenere >>> key = "encrypt" >>> ct = "QRGK kt HRZQE BPR" >>> decipher_vigenere(ct, key) 'MEETMEONMONDAY' """ symbols = "".join(symbols) A = alphabet_of_cipher(symbols) N = len(A) # normally, 26 key0 = uniq(key) key0 = [x.capitalize() for x in key0 if x.isalnum()] K = [A.index(x) for x in key0] k = len(K) ct0 = [x.capitalize() for x in ct if x.isalnum()] C = [A.index(x) for x in ct0] n = len(C) #m = n//k P = [(-K[i % k] + C[i]) % N for i in range(n)] return "".join([str(A[x]) for x in P])
def bifid6_square(key): r""" 6x6 Polybius square. Produces the Polybius square for the `6 \times 6` Bifid cipher. Assumes alphabet of symbols is "A", ..., "Z", "0", ..., "9". Examples ======== >>> from sympy.crypto.crypto import bifid6_square >>> key = "encrypt" >>> bifid6_square(key) Matrix([ [E, N, C, R, Y, P], [T, A, B, D, F, G], [H, I, J, K, L, M], [O, Q, S, U, V, W], [X, Z, 0, 1, 2, 3], [4, 5, 6, 7, 8, 9]]) """ A = alphabet_of_cipher() + [str(a) for a in range(10)] # first make sure the letters are capitalized # and text has no spaces key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] # create long key long_key = key0 + [x for x in A if not (x in key0)] f = lambda i, j: Symbol(long_key[6 * i + j]) M = Matrix(6, 6, f) return M
def __new__(cls, expr, variables, point, **assumptions): if not ordered_iter(variables, Tuple): variables = [variables] variables = Tuple(*sympify(variables)) if uniq(variables) != variables: repeated = repeated = [ v for v in set(variables) if list(variables).count(v) > 1 ] raise ValueError('cannot substitute expressions %s more than ' 'once.' % repeated) if not ordered_iter(point, Tuple): point = [point] point = Tuple(*sympify(point)) if len(point) != len(variables): raise ValueError('Number of point values must be the same as ' 'the number of variables.') # it's necessary to use dummy variables internally new_variables = Tuple(*[ arg.as_dummy() if arg.is_Symbol else C.Dummy(str(arg)) for arg in variables ]) expr = sympify(expr).subs(tuple(zip(variables, new_variables))) if expr.is_commutative: assumptions['commutative'] = True obj = Expr.__new__(cls, expr, new_variables, point, **assumptions) return obj
def bifid7_square(key): r""" 7x7 Polybius square. Produce the Polybius square for the `7 \times 7` Bifid cipher. Assumes alphabet of symbols is "A", ..., "Z", "0", ..., "22". (Also, assumes you have some way of distinguishing "22" from "2", "2" juxtaposed together for deciphering...) Examples ======== >>> from sympy.crypto.crypto import bifid7_square >>> bifid7_square("gold bug") Matrix([ [ G, O, L, D, B, U, A], [ C, E, F, H, I, J, K], [ M, N, P, Q, R, S, T], [ V, W, X, Y, Z, 0, 1], [ 2, 3, 4, 5, 6, 7, 8], [ 9, 10, 11, 12, 13, 14, 15], [16, 17, 18, 19, 20, 21, 22]]) """ A = alphabet_of_cipher() + [str(a) for a in range(23)] # first make sure the letters are capitalized # and text has no spaces key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] # create long key long_key = key0 + [x for x in A if (not (x in key0))] f = lambda i, j: Symbol(long_key[7 * i + j]) M = Matrix(7, 7, f) return M
def _run(coeffs): # find runs in coeffs such that the difference in terms (mod 1) # of t1, t2, ..., tn is 1/n u = list(uniq(coeffs)) for i in range(len(u)): dj = ([((u[j] - u[i]) % 1, j) for j in range(i + 1, len(u))]) for one, j in dj: if one.p == 1 and one.q != 1: n = one.q got = [i] get = list(range(1, n)) for d, j in dj: m = n * d if m.is_Integer and m in get: get.remove(m) got.append(j) if not get: break else: continue for i, j in enumerate(got): c = u[j] coeffs.remove(c) got[i] = c return one.q, got[0], got[1:]
def __new__(cls, expr, variables, point, **assumptions): if not ordered_iter(variables, Tuple): variables = [variables] variables = Tuple(*sympify(variables)) if uniq(variables) != variables: repeated = repeated = [ v for v in set(variables) if list(variables).count(v) > 1 ] raise ValueError('cannot substitute expressions %s more than ' 'once.' % repeated) if not ordered_iter(point, Tuple): point = [point] point = Tuple(*sympify(point)) if len(point) != len(variables): raise ValueError('Number of point values must be the same as ' 'the number of variables.') # it's necessary to use dummy variables internally new_variables = Tuple(*[ arg.as_dummy() if arg.is_Symbol else C.Dummy(str(arg)) for arg in variables ]) expr = sympify(expr).subs(tuple(zip(variables, new_variables))) if expr.is_commutative: assumptions['commutative'] = True obj = Expr.__new__(cls, expr, new_variables, point, **assumptions) return obj
def bifid5_square(key): r""" 5x5 Polybius square. Produce the Polybius square for the `5 \times 5` Bifid cipher. Examples ======== >>> from sympy.crypto.crypto import bifid5_square >>> bifid5_square("gold bug") Matrix([ [G, O, L, D, B], [U, A, C, E, F], [H, I, K, M, N], [P, Q, R, S, T], [V, W, X, Y, Z]]) """ A = alphabet_of_cipher() # first make sure the letters are capitalized # and key has no spaces or duplicates key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] # create long key long_key = key0 + [x for x in A if (not(x in key0) and x != "J")] f = lambda i, j: Symbol(long_key[5*i + j]) M = Matrix(5, 5, f) return M
def bifid5_square(key): r""" 5x5 Polybius square. Produce the Polybius square for the `5 \times 5` Bifid cipher. Examples ======== >>> from sympy.crypto.crypto import bifid5_square >>> bifid5_square("gold bug") Matrix([ [G, O, L, D, B], [U, A, C, E, F], [H, I, K, M, N], [P, Q, R, S, T], [V, W, X, Y, Z]]) """ A = alphabet_of_cipher() # first make sure the letters are capitalized # and key has no spaces or duplicates key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] # create long key long_key = key0 + [x for x in A if (not (x in key0) and x != "J")] f = lambda i, j: Symbol(long_key[5 * i + j]) M = Matrix(5, 5, f) return M
def test_uniq(): assert list(uniq(p.copy() for p in partitions(4))) == [{4: 1}, {1: 1, 3: 1}, {2: 2}, {1: 2, 2: 1}, {1: 4}] assert list(uniq(x % 2 for x in range(5))) == [0, 1] assert list(uniq("a")) == ["a"] assert list(uniq("ababc")) == list("abc") assert list(uniq([[1], [2, 1], [1]])) == [[1], [2, 1]] assert list(uniq(permutations(i for i in [[1], 2, 2]))) == [([1], 2, 2), (2, [1], 2), (2, 2, [1])] assert list(uniq([2, 3, 2, 4, [2], [1], [2], [3], [1]])) == [2, 3, 4, [2], [1], [3]]
def test_uniq(): assert list(uniq(p for p in partitions(4))) == \ [{4: 1}, {1: 1, 3: 1}, {2: 2}, {1: 2, 2: 1}, {1: 4}] assert list(uniq(x % 2 for x in range(5))) == [0, 1] assert list(uniq('a')) == ['a'] assert list(uniq('ababc')) == list('abc') assert list(uniq([[1], [2, 1], [1]])) == [[1], [2, 1]] assert list(uniq(permutations(i for i in [[1], 2, 2]))) == \ [([1], 2, 2), (2, [1], 2), (2, 2, [1])] assert list(uniq([2, 3, 2, 4, [2], [1], [2], [3], [1]])) == \ [2, 3, 4, [2], [1], [3]] f = [1] raises(RuntimeError, lambda: [f.remove(i) for i in uniq(f)]) f = [[1]] raises(RuntimeError, lambda: [f.remove(i) for i in uniq(f)])
def _solve_even_degree_expr(expr, t, symbol, domain=S.Complexes): """Return the unique solutions of equations derived from ``expr`` by replacing ``t`` with ``+/- symbol``. Parameters ========== expr : Expr The expression which includes a dummy variable t to be replaced with +symbol and -symbol. symbol : Symbol The symbol for which a solution is being sought. Returns ======= List of unique solution of the two equations generated by replacing ``t`` with positive and negative ``symbol``. Notes ===== If ``expr = 2*log(t) + x/2` then solutions for ``2*log(x) + x/2 = 0`` and ``2*log(-x) + x/2 = 0`` are returned by this function. Though this may seem counter-intuitive, one must note that the ``expr`` being solved here has been derived from a different expression. For an expression like ``eq = x**2*g(x) = 1``, if we take the log of both sides we obtain ``log(x**2) + log(g(x)) = 0``. If x is positive then this simplifies to ``2*log(x) + log(g(x)) = 0``; the Lambert-solving routines will return solutions for this, but we must also consider the solutions for ``2*log(-x) + log(g(x))`` since those must also be a solution of ``eq`` which has the same value when the ``x`` in ``x**2`` is negated. If `g(x)` does not have even powers of symbol then we don't want to replace the ``x`` there with ``-x``. So the role of the ``t`` in the expression received by this function is to mark where ``+/-x`` should be inserted before obtaining the Lambert solutions. """ nlhs, plhs = [ expr.xreplace({t: sgn*symbol}) for sgn in (-1, 1)] sols = _solve_lambert(nlhs, symbol, gens, domain) if sols == S.EmptySet: return S.EmptySet if plhs != nlhs: sols.extend(_solve_lambert(plhs, symbol, gens, domain)) # uniq is needed for a case like # 2*log(t) - log(-z**2) + log(z + log(x) + log(z)) # where subtituting t with +/-x gives all the same solution; # uniq, rather than list(set()), is used to maintain canonical # order return list(uniq(sols))
def is_concyclic(self, *args): """Do `self` and the given sequence of points lie in a circle? Returns True if the set of points are concyclic and False otherwise. A trivial value of True is returned if there are fewer than 2 other points. Parameters ========== args : sequence of Points Returns ======= is_concyclic : boolean Examples ======== >>> from sympy import Point Define 4 points that are on the unit circle: >>> p1, p2, p3, p4 = Point(1, 0), (0, 1), (-1, 0), (0, -1) >>> p1.is_concyclic() == p1.is_concyclic(p2, p3, p4) == True True Define a point not on that circle: >>> p = Point(1, 1) >>> p.is_concyclic(p1, p2, p3) False """ points = (self,) + args points = Point._normalize_dimension(*[Point(i) for i in points]) points = list(uniq(points)) if not Point.affine_rank(*points) <= 2: return False origin = points[0] points = [p - origin for p in points] # points are concyclic if they are coplanar and # there is a point c so that ||p_i-c|| == ||p_j-c|| for all # i and j. Rearranging this equation gives us the following # condition: the matrix `mat` must not a pivot in the last # column. mat = Matrix([list(i) + [i.dot(i)] for i in points]) rref, pivots = mat.rref() if len(origin) not in pivots: return True return False
def is_concyclic(self, *args): """Do `self` and the given sequence of points lie in a circle? Returns True if the set of points are concyclic and False otherwise. A trivial value of True is returned if there are fewer than 2 other points. Parameters ========== args : sequence of Points Returns ======= is_concyclic : boolean Examples ======== >>> from sympy import Point Define 4 points that are on the unit circle: >>> p1, p2, p3, p4 = Point(1, 0), (0, 1), (-1, 0), (0, -1) >>> p1.is_concyclic() == p1.is_concyclic(p2, p3, p4) == True True Define a point not on that circle: >>> p = Point(1, 1) >>> p.is_concyclic(p1, p2, p3) False """ points = (self,) + args points = Point._normalize_dimension(*[Point(i) for i in points]) points = list(uniq(points)) if not Point.affine_rank(*points) <= 2: return False origin = points[0] points = [p - origin for p in points] # points are concyclic if they are coplanar and # there is a point c so that ||p_i-c|| == ||p_j-c|| for all # i and j. Rearranging this equation gives us the following # condition: the matrix `mat` must not a pivot in the last # column. mat = Matrix([list(i) + [i.dot(i)] for i in points]) rref, pivots = mat.rref() if len(origin) not in pivots: return True return False
def DirectProduct(*groups): """ Returns the direct product of several groups as a permutation group. Explanation =========== This is implemented much like the __mul__ procedure for taking the direct product of two permutation groups, but the idea of shifting the generators is realized in the case of an arbitrary number of groups. A call to DirectProduct(G1, G2, ..., Gn) is generally expected to be faster than a call to G1*G2*...*Gn (and thus the need for this algorithm). Examples ======== >>> from sympy.combinatorics.group_constructs import DirectProduct >>> from sympy.combinatorics.named_groups import CyclicGroup >>> C = CyclicGroup(4) >>> G = DirectProduct(C, C, C) >>> G.order() 64 See Also ======== sympy.combinatorics.perm_groups.PermutationGroup.__mul__ """ degrees = [] gens_count = [] total_degree = 0 total_gens = 0 for group in groups: current_deg = group.degree current_num_gens = len(group.generators) degrees.append(current_deg) total_degree += current_deg gens_count.append(current_num_gens) total_gens += current_num_gens array_gens = [] for i in range(total_gens): array_gens.append(list(range(total_degree))) current_gen = 0 current_deg = 0 for i in range(len(gens_count)): for j in range(current_gen, current_gen + gens_count[i]): gen = ((groups[i].generators)[j - current_gen]).array_form array_gens[j][current_deg:current_deg + degrees[i]] = \ [x + current_deg for x in gen] current_gen += gens_count[i] current_deg += degrees[i] perm_gens = list(uniq([_af_new(list(a)) for a in array_gens])) return PermutationGroup(perm_gens, dups=False)
def _do_ellipse_intersection(self, o): """The intersection of an ellipse with another ellipse or a circle. Private helper method for `intersection`. """ x = Dummy('x', real=True) y = Dummy('y', real=True) seq = self.equation(x, y) oeq = o.equation(x, y) result = solve([seq, oeq], [x, y]) return [Point(*r) for r in list(uniq(result))]
def _do_ellipse_intersection(self, o): """The intersection of an ellipse with another ellipse or a circle. Private helper method for `intersection`. """ x = Dummy('x', real=True) y = Dummy('y', real=True) seq = self.equation(x, y) oeq = o.equation(x, y) result = solve([seq, oeq], [x, y]) return [Point(*r) for r in list(uniq(result))]
def test_uniq(): assert list(uniq(p.copy() for p in partitions(4))) == \ [{4: 1}, {1: 1, 3: 1}, {2: 2}, {1: 2, 2: 1}, {1: 4}] assert list(uniq(x % 2 for x in range(5))) == [0, 1] assert list(uniq('a')) == ['a'] assert list(uniq('ababc')) == list('abc') assert list(uniq([[1], [2, 1], [1]])) == [[1], [2, 1]] assert list(uniq(permutations(i for i in [[1], 2, 2]))) == \ [([1], 2, 2), (2, [1], 2), (2, 2, [1])] assert list(uniq([2, 3, 2, 4, [2], [1], [2], [3], [1]])) == \ [2, 3, 4, [2], [1], [3]]
def _do_ellipse_intersection(self, o): """The intersection of an ellipse with another ellipse or a circle. Private helper method for `intersection`. """ x = Dummy('x', real=True) y = Dummy('y', real=True) seq = self.equation(x, y) oeq = o.equation(x, y) # TODO: Replace solve with nonlinsolve, when nonlinsolve will be able to solve in real domain result = solve([seq, oeq], x, y) return [Point(*r) for r in list(uniq(result))]
def DirectProduct(*groups): """ Returns the direct product of several groups as a permutation group. This is implemented much like the __mul__ procedure for taking the direct product of two permutation groups, but the idea of shifting the generators is realized in the case of an arbitrary number of groups. A call to DirectProduct(G1, G2, ..., Gn) is generally expected to be faster than a call to G1*G2*...*Gn (and thus the need for this algorithm). Examples ======== >>> from sympy.combinatorics.group_constructs import DirectProduct >>> from sympy.combinatorics.named_groups import CyclicGroup >>> C = CyclicGroup(4) >>> G = DirectProduct(C, C, C) >>> G.order() 64 See Also ======== __mul__ """ degrees = [] gens_count = [] total_degree = 0 total_gens = 0 for group in groups: current_deg = group.degree current_num_gens = len(group.generators) degrees.append(current_deg) total_degree += current_deg gens_count.append(current_num_gens) total_gens += current_num_gens array_gens = [] for i in range(total_gens): array_gens.append(list(range(total_degree))) current_gen = 0 current_deg = 0 for i in range(len(gens_count)): for j in range(current_gen, current_gen + gens_count[i]): gen = ((groups[i].generators)[j - current_gen]).array_form array_gens[j][current_deg:current_deg + degrees[i]] = \ [x + current_deg for x in gen] current_gen += gens_count[i] current_deg += degrees[i] perm_gens = list(uniq([_af_new(list(a)) for a in array_gens])) return PermutationGroup(perm_gens, dups=False)
def test_uniq(): assert list(uniq(p.copy() for p in partitions(4))) == \ [{4: 1}, {1: 1, 3: 1}, {2: 2}, {1: 2, 2: 1}, {1: 4}] assert list(uniq(x % 2 for x in range(5))) == [0, 1] assert list(uniq('a')) == ['a'] assert list(uniq('ababc')) == list('abc') assert list(uniq([[1], [2, 1], [1]])) == [[1], [2, 1], [1]] assert list(uniq(permutations(i for i in [[1], 2, 2]))) == \ [([1], 2, 2), (2, [1], 2), (2, 2, [1]), (2, [1], 2), (2, 2, [1])]
def __eq__(self, other): if not isinstance(other, Subs): return False if (len(self.point) != len(other.point) or self.free_symbols != other.free_symbols or sorted(self.point) != sorted(other.point)): return False # non-repeated point args selfargs = [ v[0] for v in sorted(zip(self.variables, self.point), key=lambda v: v[1]) if list(self.point.args).count(v[1]) == 1 ] otherargs = [ v[0] for v in sorted(zip(other.variables, other.point), key=lambda v: v[1]) if list(other.point.args).count(v[1]) == 1 ] # find repeated point values and subs each associated variable # for a single symbol selfrepargs = [] otherrepargs = [] if uniq(self.point) != self.point: repeated = uniq( [v for v in self.point if list(self.point.args).count(v) > 1]) repswap = dict( zip(repeated, [C.Dummy() for _ in xrange(len(repeated))])) selfrepargs = [(self.variables[i], repswap[v]) for i, v in enumerate(self.point) if v in repeated] otherrepargs = [(other.variables[i], repswap[v]) for i, v in enumerate(other.point) if v in repeated] return self.expr.subs(selfrepargs) == other.expr.subs( tuple(zip(otherargs, selfargs))).subs(otherrepargs)
def are_concurrent(*planes): """Is a sequence of Planes concurrent? Two or more Planes are concurrent if their intersections are a common line. Parameters ========== planes: list Returns ======= Boolean Examples ======== >>> from sympy import Plane, Point3D >>> a = Plane(Point3D(5, 0, 0), normal_vector=(1, -1, 1)) >>> b = Plane(Point3D(0, -2, 0), normal_vector=(3, 1, 1)) >>> c = Plane(Point3D(0, -1, 0), normal_vector=(5, -1, 9)) >>> Plane.are_concurrent(a, b) True >>> Plane.are_concurrent(a, b, c) False """ planes = list(uniq(planes)) for i in planes: if not isinstance(i, Plane): raise ValueError("All objects should be Planes but got %s" % i.func) if len(planes) < 2: return False planes = list(planes) first = planes.pop(0) sol = first.intersection(planes[0]) if sol == []: return False else: line = sol[0] for i in planes[1:]: l = first.intersection(i) if not l or not l[0] in line: return False return True
def decipher_bifid6(ct, key): r""" Performs the Bifid cipher decryption on ciphertext ``ct``, and returns the plaintext. This is the version of the Bifid cipher that uses the `6 \times 6` Polybius square. Assumes alphabet of symbols is "A", ..., "Z", "0", ..., "9". INPUT: ``ct``: ciphertext string (digits okay) ``key``: short string for key (no repetitions, digits okay) OUTPUT: plaintext from Bifid cipher (all caps, no spaces) Examples ======== >>> from sympy.crypto.crypto import encipher_bifid6, decipher_bifid6 >>> key = "encrypt" >>> pt = "meet me on monday at 8am" >>> encipher_bifid6(pt, key) 'HNHOKNTA5MEPEGNQZYG' >>> ct = "HNHOKNTA5MEPEGNQZYG" >>> decipher_bifid6(ct, key) 'MEETMEONMONDAYAT8AM' """ A = alphabet_of_cipher() + [str(a) for a in range(10)] # first make sure the letters are capitalized # and text has no spaces key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] ct0 = [x.capitalize() for x in ct if x.isalnum()] # create long key long_key = key0 + [x for x in A if not (x in key0)] n = len(ct0) # the fractionalization pairs = flatten([[long_key.index(x) // 6, long_key.index(x) % 6] for x in ct0]) tmp_plain = flatten([[pairs[i], pairs[n + i]] for i in range(n)]) pt = "".join([ long_key[6 * tmp_plain[2 * i] + tmp_plain[2 * i + 1]] for i in range(n) ]) return pt
def are_concurrent(*planes): """Is a sequence of Planes concurrent? Two or more Planes are concurrent if their intersections are a common line. Parameters ========== planes: list Returns ======= Boolean Examples ======== >>> from sympy import Plane, Point3D >>> a = Plane(Point3D(5, 0, 0), normal_vector=(1, -1, 1)) >>> b = Plane(Point3D(0, -2, 0), normal_vector=(3, 1, 1)) >>> c = Plane(Point3D(0, -1, 0), normal_vector=(5, -1, 9)) >>> Plane.are_concurrent(a, b) True >>> Plane.are_concurrent(a, b, c) False """ planes = list(uniq(planes)) for i in planes: if not isinstance(i, Plane): raise ValueError('All objects should be Planes but got %s' % i.func) if len(planes) < 2: return False planes = list(planes) first = planes.pop(0) sol = first.intersection(planes[0]) if sol == []: return False else: line = sol[0] for i in planes[1:]: l = first.intersection(i) if not l or not l[0] in line: return False return True
def decipher_bifid5(ct, key): r""" Performs the Bifid cipher decryption on ciphertext ``ct``, and returns the plaintext. This is the version of the Bifid cipher that uses the `5 \times 5` Polybius square. INPUT: ``ct``: ciphertext string (digits okay) ``key``: short string for key (no repetitions, digits okay) OUTPUT: plaintext from Bifid5 cipher (all caps, no spaces, no "J"s) Examples ======== >>> from sympy.crypto.crypto import encipher_bifid5, decipher_bifid5 >>> key = "encrypt" >>> pt = "meet me on monday" >>> encipher_bifid5(pt, key) 'LNLLQNPPNPGADK' >>> ct = 'LNLLQNPPNPGADK' >>> decipher_bifid5(ct, key) 'MEETMEONMONDAY' """ A = alphabet_of_cipher() # first make sure the letters are capitalized # and text has no spaces key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] ct0 = [x.capitalize() for x in ct if x.isalnum()] # create long key long_key = key0 + [x for x in A if (not (x in key0) and x != "J")] n = len(ct0) # the fractionalization pairs = flatten([[long_key.index(x) // 5, long_key.index(x) % 5] for x in ct0 if x != "J"]) tmp_plain = flatten([[pairs[i], pairs[n + i]] for i in range(n)]) pt = "".join([ long_key[5 * tmp_plain[2 * i] + tmp_plain[2 * i + 1]] for i in range(n) ]) return pt
def encipher_bifid7(pt, key): r""" Performs the Bifid cipher encryption on plaintext ``pt``, and returns the ciphertext. This is the version of the Bifid cipher that uses the `7 \times 7` Polybius square. Assumes alphabet of symbols is "A", ..., "Z", "0", ..., "22". (Also, assumes you have some way of distinguishing "22" from "2", "2" juxtaposed together for deciphering...) INPUT: ``pt``: plaintext string (digits okay) ``key``: short string for key (no repetitions, digits okay) OUTPUT: ciphertext from Bifid7 cipher (all caps, no spaces) Examples ======== >>> from sympy.crypto.crypto import encipher_bifid7 >>> key = "encrypt" >>> pt = "meet me on monday at 8am" >>> encipher_bifid7(pt, key) 'JEJJLNAA3ME19YF3J222R' """ A = alphabet_of_cipher() + [str(a) for a in range(23)] # first make sure the letters are capitalized # and text has no spaces key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] pt0 = [x.capitalize() for x in pt if x.isalnum()] # create long key long_key = key0 + [x for x in A if not (x in key0)] n = len(pt0) # the fractionalization pairs = [[long_key.index(x) // 7, long_key.index(x) % 7] for x in pt0] tmp_cipher = flatten([x[0] for x in pairs] + [x[1] for x in pairs]) ct = "".join([ long_key[7 * tmp_cipher[2 * i] + tmp_cipher[2 * i + 1]] for i in range(n) ]) return ct
def are_coplanar(cls, *points): """Return True if there exists a plane in which all the points lie. A trivial True value is returned if `len(points) < 3` or all Points are 2-dimensional. Parameters ========== A set of points Raises ====== ValueError : if less than 3 unique points are given Returns ======= boolean Examples ======== >>> from sympy import Point3D >>> p1 = Point3D(1, 2, 2) >>> p2 = Point3D(2, 7, 2) >>> p3 = Point3D(0, 0, 2) >>> p4 = Point3D(1, 1, 2) >>> Point3D.are_coplanar(p1, p2, p3, p4) True >>> p5 = Point3D(0, 1, 3) >>> Point3D.are_coplanar(p1, p2, p3, p5) False """ if len(points) <= 1: return True points = cls._normalize_dimension(*[Point(i) for i in points]) # quick exit if we are in 2D if points[0].ambient_dimension == 2: return True points = list(uniq(points)) return Point.affine_rank(*points) <= 2
def decipher_bifid6(ct, key): r""" Performs the Bifid cipher decryption on ciphertext ``ct``, and returns the plaintext. This is the version of the Bifid cipher that uses the `6 \times 6` Polybius square. Assumes alphabet of symbols is "A", ..., "Z", "0", ..., "9". INPUT: ``ct``: ciphertext string (digits okay) ``key``: short string for key (no repetitions, digits okay) OUTPUT: plaintext from Bifid cipher (all caps, no spaces) Examples ======== >>> from sympy.crypto.crypto import encipher_bifid6, decipher_bifid6 >>> key = "encrypt" >>> pt = "meet me on monday at 8am" >>> encipher_bifid6(pt, key) 'HNHOKNTA5MEPEGNQZYG' >>> ct = "HNHOKNTA5MEPEGNQZYG" >>> decipher_bifid6(ct, key) 'MEETMEONMONDAYAT8AM' """ A = alphabet_of_cipher() + [str(a) for a in range(10)] # first make sure the letters are capitalized # and text has no spaces key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] ct0 = [x.capitalize() for x in ct if x.isalnum()] # create long key long_key = key0 + [x for x in A if not(x in key0)] n = len(ct0) # the fractionalization pairs = flatten([[long_key.index(x)//6, long_key.index(x) % 6] for x in ct0]) tmp_plain = flatten([[pairs[i], pairs[n + i]] for i in range(n)]) pt = "".join([long_key[6*tmp_plain[2*i] + tmp_plain[2*i + 1]] for i in range(n)]) return pt
def are_coplanar(cls, *points): """Return True if there exists a plane in which all the points lie. A trivial True value is returned if `len(points) < 3` or all Points are 2-dimensional. Parameters ========== A set of points Raises ====== ValueError : if less than 3 unique points are given Returns ======= boolean Examples ======== >>> from sympy import Point3D >>> p1 = Point3D(1, 2, 2) >>> p2 = Point3D(2, 7, 2) >>> p3 = Point3D(0, 0, 2) >>> p4 = Point3D(1, 1, 2) >>> Point3D.are_coplanar(p1, p2, p3, p4) True >>> p5 = Point3D(0, 1, 3) >>> Point3D.are_coplanar(p1, p2, p3, p5) False """ if len(points) <= 1: return True points = cls._normalize_dimension(*[Point(i) for i in points]) # quick exit if we are in 2D if points[0].ambient_dimension == 2: return True points = list(uniq(points)) return Point.affine_rank(*points) <= 2
def encipher_bifid7(pt, key): r""" Performs the Bifid cipher encryption on plaintext ``pt``, and returns the ciphertext. This is the version of the Bifid cipher that uses the `7 \times 7` Polybius square. Assumes alphabet of symbols is "A", ..., "Z", "0", ..., "22". (Also, assumes you have some way of distinguishing "22" from "2", "2" juxtaposed together for deciphering...) INPUT: ``pt``: plaintext string (digits okay) ``key``: short string for key (no repetitions, digits okay) OUTPUT: ciphertext from Bifid7 cipher (all caps, no spaces) Examples ======== >>> from sympy.crypto.crypto import encipher_bifid7 >>> key = "encrypt" >>> pt = "meet me on monday at 8am" >>> encipher_bifid7(pt, key) 'JEJJLNAA3ME19YF3J222R' """ A = alphabet_of_cipher() + [str(a) for a in range(23)] # first make sure the letters are capitalized # and text has no spaces key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] pt0 = [x.capitalize() for x in pt if x.isalnum()] # create long key long_key = key0 + [x for x in A if not(x in key0)] n = len(pt0) # the fractionalization pairs = [[long_key.index(x)//7, long_key.index(x) % 7] for x in pt0] tmp_cipher = flatten([x[0] for x in pairs] + [x[1] for x in pairs]) ct = "".join([long_key[7*tmp_cipher[2*i] + tmp_cipher[2*i + 1]] for i in range(n)]) return ct
def decipher_bifid5(ct, key): r""" Performs the Bifid cipher decryption on ciphertext ``ct``, and returns the plaintext. This is the version of the Bifid cipher that uses the `5 \times 5` Polybius square. INPUT: ``ct``: ciphertext string (digits okay) ``key``: short string for key (no repetitions, digits okay) OUTPUT: plaintext from Bifid5 cipher (all caps, no spaces, no "J"s) Examples ======== >>> from sympy.crypto.crypto import encipher_bifid5, decipher_bifid5 >>> key = "encrypt" >>> pt = "meet me on monday" >>> encipher_bifid5(pt, key) 'LNLLQNPPNPGADK' >>> ct = 'LNLLQNPPNPGADK' >>> decipher_bifid5(ct, key) 'MEETMEONMONDAY' """ A = alphabet_of_cipher() # first make sure the letters are capitalized # and text has no spaces key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] ct0 = [x.capitalize() for x in ct if x.isalnum()] # create long key long_key = key0 + [x for x in A if (not(x in key0) and x != "J")] n = len(ct0) # the fractionalization pairs = flatten([[long_key.index(x)//5, long_key.index(x) % 5] for x in ct0 if x != "J"]) tmp_plain = flatten([[pairs[i], pairs[n + i]] for i in range(n)]) pt = "".join([long_key[5*tmp_plain[2*i] + tmp_plain[2*i + 1]] for i in range(n)]) return pt
def test_uniq(): assert list(uniq(p.copy() for p in partitions(4))) == [ {4: 1}, {1: 1, 3: 1}, {2: 2}, {1: 2, 2: 1}, {1: 4}, ] assert list(uniq(x % 2 for x in range(5))) == [0, 1] assert list(uniq("a")) == ["a"] assert list(uniq("ababc")) == list("abc") assert list(uniq([[1], [2, 1], [1]])) == [[1], [2, 1]] assert list(uniq(permutations(i for i in [[1], 2, 2]))) == [ ([1], 2, 2), (2, [1], 2), (2, 2, [1]), ] assert list(uniq([2, 3, 2, 4, [2], [1], [2], [3], [1]])) == [2, 3, 4, [2], [1], [3]]
def is_collinear(self, *args): """Returns `True` if there exists a line that contains `self` and `points`. Returns `False` otherwise. A trivially True value is returned if no points are given. Parameters ========== args : sequence of Points Returns ======= is_collinear : boolean See Also ======== sympy.geometry.line.Line Examples ======== >>> from sympy import Point >>> from sympy.abc import x >>> p1, p2 = Point(0, 0), Point(1, 1) >>> p3, p4, p5 = Point(2, 2), Point(x, x), Point(1, 2) >>> Point.is_collinear(p1, p2, p3, p4) True >>> Point.is_collinear(p1, p2, p3, p5) False """ points = (self,) + args points = Point._normalize_dimension(*[Point(i) for i in points]) points = list(uniq(points)) return Point.affine_rank(*points) <= 1
def is_collinear(self, *args): """Returns `True` if there exists a line that contains `self` and `points`. Returns `False` otherwise. A trivially True value is returned if no points are given. Parameters ========== args : sequence of Points Returns ======= is_collinear : boolean See Also ======== sympy.geometry.line.Line Examples ======== >>> from sympy import Point >>> from sympy.abc import x >>> p1, p2 = Point(0, 0), Point(1, 1) >>> p3, p4, p5 = Point(2, 2), Point(x, x), Point(1, 2) >>> Point.is_collinear(p1, p2, p3, p4) True >>> Point.is_collinear(p1, p2, p3, p5) False """ points = (self,) + args points = Point._normalize_dimension(*[Point(i) for i in points]) points = list(uniq(points)) return Point.affine_rank(*points) <= 1
def heurisch_wrapper(f, x, rewrite=False, hints=None, mappings=None, retries=3, degree_offset=0, unnecessary_permutations=None): """ A wrapper around the heurisch integration algorithm. This method takes the result from heurisch and checks for poles in the denominator. For each of these poles, the integral is reevaluated, and the final integration result is given in terms of a Piecewise. Examples ======== >>> from sympy.core import symbols >>> from sympy.functions import cos >>> from sympy.integrals.heurisch import heurisch, heurisch_wrapper >>> n, x = symbols('n x') >>> heurisch(cos(n*x), x) sin(n*x)/n >>> heurisch_wrapper(cos(n*x), x) Piecewise((x, n == 0), (sin(n*x)/n, True)) See Also ======== heurisch """ f = sympify(f) if x not in f.free_symbols: return f*x res = heurisch(f, x, rewrite, hints, mappings, retries, degree_offset, unnecessary_permutations) if not isinstance(res, Basic): return res # We consider each denominator in the expression, and try to find # cases where one or more symbolic denominator might be zero. The # conditions for these cases are stored in the list slns. slns = [] for d in denoms(res): try: slns += solve(d, dict=True, exclude=(x,)) except NotImplementedError: pass if not slns: return res slns = list(uniq(slns)) # Remove the solutions corresponding to poles in the original expression. slns0 = [] for d in denoms(f): try: slns0 += solve(d, dict=True, exclude=(x,)) except NotImplementedError: pass slns = [s for s in slns if s not in slns0] if not slns: return res if len(slns) > 1: eqs = [] for sub_dict in slns: eqs.extend([Eq(key, value) for key, value in sub_dict.items()]) slns = solve(eqs, dict=True, exclude=(x,)) + slns # For each case listed in the list slns, we reevaluate the integral. pairs = [] for sub_dict in slns: expr = heurisch(f.subs(sub_dict), x, rewrite, hints, mappings, retries, degree_offset, unnecessary_permutations) cond = And(*[Eq(key, value) for key, value in sub_dict.items()]) pairs.append((expr, cond)) pairs.append((heurisch(f, x, rewrite, hints, mappings, retries, degree_offset, unnecessary_permutations), True)) return Piecewise(*pairs)
def test_uniq(): assert list(uniq(p.copy() for p in partitions(4))) == \ [{4: 1}, {1: 1, 3: 1}, {2: 2}, {1: 2, 2: 1}, {1: 4}] assert list(uniq(x % 2 for x in range(5))) == [0, 1] assert list(uniq('a')) == ['a'] assert list(uniq('ababc')) == list('abc')
def _intervals(self, sym): """Return a list of unique tuples, (a, b, e, i), where a and b are the lower and upper bounds in which the expression e of argument i in self is defined and a < b (when involving numbers) or a <= b when involving symbols. If there are any relationals not involving sym, or any relational cannot be solved for sym, NotImplementedError is raised. The calling routine should have removed such relationals before calling this routine. The evaluated conditions will be returned as ranges. Discontinuous ranges will be returned separately with identical expressions. The first condition that evaluates to True will be returned as the last tuple with a, b = -oo, oo. """ from sympy.solvers.inequalities import _solve_inequality from sympy.logic.boolalg import to_cnf, distribute_or_over_and assert isinstance(self, Piecewise) def _solve_relational(r): if sym not in r.free_symbols: nonsymfail(r) rv = _solve_inequality(r, sym) if isinstance(rv, Relational): free = rv.args[1].free_symbols if rv.args[0] != sym or sym in free: raise NotImplementedError( filldedent(''' Unable to solve relational %s for %s.''' % (r, sym))) if rv.rel_op == '==': # this equality has been affirmed to have the form # Eq(sym, rhs) where rhs is sym-free; it represents # a zero-width interval which will be ignored # whether it is an isolated condition or contained # within an And or an Or rv = S.false elif rv.rel_op == '!=': try: rv = Or(sym < rv.rhs, sym > rv.rhs) except TypeError: # e.g. x != I ==> all real x satisfy rv = S.true elif rv == (S.NegativeInfinity < sym) & (sym < S.Infinity): rv = S.true return rv def nonsymfail(cond): raise NotImplementedError( filldedent(''' A condition not involving %s appeared: %s''' % (sym, cond))) # make self canonical wrt Relationals reps = dict([(r, _solve_relational(r)) for r in self.atoms(Relational)]) # process args individually so if any evaluate, their position # in the original Piecewise will be known args = [i.xreplace(reps) for i in self.args] # precondition args expr_cond = [] default = idefault = None for i, (expr, cond) in enumerate(args): if cond is S.false: continue elif cond is S.true: default = expr idefault = i break cond = to_cnf(cond) if isinstance(cond, And): cond = distribute_or_over_and(cond) if isinstance(cond, Or): expr_cond.extend([(i, expr, o) for o in cond.args if not isinstance(o, Equality)]) elif cond is not S.false: expr_cond.append((i, expr, cond)) # determine intervals represented by conditions int_expr = [] for iarg, expr, cond in expr_cond: if isinstance(cond, And): lower = S.NegativeInfinity upper = S.Infinity for cond2 in cond.args: if isinstance(cond2, Equality): lower = upper # ignore break elif cond2.lts == sym: upper = Min(cond2.gts, upper) elif cond2.gts == sym: lower = Max(cond2.lts, lower) else: nonsymfail(cond2) # should never get here elif isinstance(cond, Relational): lower, upper = cond.lts, cond.gts # part 1: initialize with givens if cond.lts == sym: # part 1a: expand the side ... lower = S.NegativeInfinity # e.g. x <= 0 ---> -oo <= 0 elif cond.gts == sym: # part 1a: ... that can be expanded upper = S.Infinity # e.g. x >= 0 ---> oo >= 0 else: nonsymfail(cond) else: raise NotImplementedError('unrecognized condition: %s' % cond) lower, upper = lower, Max(lower, upper) if (lower >= upper) is not S.true: int_expr.append((lower, upper, expr, iarg)) if default is not None: int_expr.append( (S.NegativeInfinity, S.Infinity, default, idefault)) return list(uniq(int_expr))
def _handle_irel(self, x, handler): """Return either None (if the conditions of self depend only on x) else a Piecewise expression whose expressions (handled by the handler that was passed) are paired with the governing x-independent relationals, e.g. Piecewise((A, a(x) & b(y)), (B, c(x) | c(y)) -> Piecewise( (handler(Piecewise((A, a(x) & True), (B, c(x) | True)), b(y) & c(y)), (handler(Piecewise((A, a(x) & True), (B, c(x) | False)), b(y)), (handler(Piecewise((A, a(x) & False), (B, c(x) | True)), c(y)), (handler(Piecewise((A, a(x) & False), (B, c(x) | False)), True)) """ # identify governing relationals rel = self.atoms(Relational) irel = list( ordered([ r for r in rel if x not in r.free_symbols and r not in (S.true, S.false) ])) if irel: args = {} exprinorder = [] for truth in product((1, 0), repeat=len(irel)): reps = dict(zip(irel, truth)) # only store the true conditions since the false are implied # when they appear lower in the Piecewise args if 1 not in truth: cond = None # flag this one so it doesn't get combined else: andargs = Tuple(*[i for i in reps if reps[i]]) free = list(andargs.free_symbols) if len(free) == 1: from sympy.solvers.inequalities import ( reduce_inequalities, _solve_inequality) try: t = reduce_inequalities(andargs, free[0]) # ValueError when there are potentially # nonvanishing imaginary parts except (ValueError, NotImplementedError): # at least isolate free symbol on left t = And(*[ _solve_inequality(a, free[0], linear=True) for a in andargs ]) else: t = And(*andargs) if t is S.false: continue # an impossible combination cond = t expr = handler(self.xreplace(reps)) if isinstance(expr, self.func) and len(expr.args) == 1: expr, econd = expr.args[0] cond = And(econd, True if cond is None else cond) # the ec pairs are being collected since all possibilities # are being enumerated, but don't put the last one in since # its expr might match a previous expression and it # must appear last in the args if cond is not None: args.setdefault(expr, []).append(cond) # but since we only store the true conditions we must maintain # the order so that the expression with the most true values # comes first exprinorder.append(expr) # convert collected conditions as args of Or for k in args: args[k] = Or(*args[k]) # take them in the order obtained args = [(e, args[e]) for e in uniq(exprinorder)] # add in the last arg args.append((expr, True)) # if any condition reduced to True, it needs to go last # and there should only be one of them or else the exprs # should agree trues = [i for i in range(len(args)) if args[i][1] is S.true] if not trues: # make the last one True since all cases were enumerated e, c = args[-1] args[-1] = (e, S.true) else: assert len(set([e for e, c in [args[i] for i in trues]])) == 1 args.append(args.pop(trues.pop())) while trues: args.pop(trues.pop()) return Piecewise(*args)
def encipher_bifid5(pt, key): r""" Performs the Bifid cipher encryption on plaintext ``pt``, and returns the ciphertext. This is the version of the Bifid cipher that uses the `5 \times 5` Polybius square. Notes ===== The Bifid cipher was invented around 1901 by Felix Delastelle. It is a *fractional substitution* cipher, where letters are replaced by pairs of symbols from a smaller alphabet. The cipher uses a `5 \times 5` square filled with some ordering of the alphabet, except that "i"s and "j"s are identified (this is a so-called Polybius square; there is a `6 \times 6` analog if you add back in "j" and also append onto the usual 26 letter alphabet, the digits 0, 1, ..., 9). According to Helen Gaines' book *Cryptanalysis*, this type of cipher was used in the field by the German Army during World War I. ALGORITHM: (5x5 case) INPUT: ``pt``: plaintext string (no "j"s) ``key``: short string for key (no repetitions, no "j"s) OUTPUT: ciphertext (using Bifid5 cipher in all caps, no spaces, no "J"s) STEPS: 1. Create the `5 \times 5` Polybius square ``S`` associated to the ``k`` as follows: a) starting top left, moving left-to-right, top-to-bottom, place the letters of the key into a 5x5 matrix, b) when finished, add the letters of the alphabet not in the key until the 5x5 square is filled 2. Create a list ``P`` of pairs of numbers which are the coordinates in the Polybius square of the letters in ``pt``. 3. Let ``L1`` be the list of all first coordinates of ``P`` (length of ``L1 = n``), let ``L2`` be the list of all second coordinates of ``P`` (so the length of ``L2`` is also ``n``). 4. Let ``L`` be the concatenation of ``L1`` and ``L2`` (length ``L = 2*n``), except that consecutive numbers are paired ``(L[2*i], L[2*i + 1])``. You can regard ``L`` as a list of pairs of length ``n``. 5. Let ``C`` be the list of all letters which are of the form ``S[i, j]``, for all ``(i, j)`` in ``L``. As a string, this is the ciphertext ``ct``. Examples ======== >>> from sympy.crypto.crypto import encipher_bifid5 >>> pt = "meet me on monday" >>> key = "encrypt" >>> encipher_bifid5(pt, key) 'LNLLQNPPNPGADK' >>> pt = "meet me on friday" >>> encipher_bifid5(pt, key) 'LNLLFGPPNPGRSK' """ A = alphabet_of_cipher() # first make sure the letters are capitalized # and text has no spaces key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] pt0 = [x.capitalize() for x in pt if x.isalnum()] # create long key long_key = key0 + [x for x in A if (not(x in key0) and x != "J")] n = len(pt0) # the fractionalization pairs = [[long_key.index(x)//5, long_key.index(x) % 5] for x in pt0] tmp_cipher = flatten([x[0] for x in pairs] + [x[1] for x in pairs]) ct = "".join([long_key[5*tmp_cipher[2*i] + tmp_cipher[2*i + 1]] for i in range(n)]) return ct
def _handle_irel(self, x, handler): """Return either None (if the conditions of self depend only on x) else a Piecewise expression whose expressions (handled by the handler that was passed) are paired with the governing x-independent relationals, e.g. Piecewise((A, a(x) & b(y)), (B, c(x) | c(y)) -> Piecewise( (handler(Piecewise((A, a(x) & True), (B, c(x) | True)), b(y) & c(y)), (handler(Piecewise((A, a(x) & True), (B, c(x) | False)), b(y)), (handler(Piecewise((A, a(x) & False), (B, c(x) | True)), c(y)), (handler(Piecewise((A, a(x) & False), (B, c(x) | False)), True)) """ # identify governing relationals rel = self.atoms(Relational) irel = list(ordered([r for r in rel if x not in r.free_symbols and r not in (S.true, S.false)])) if irel: args = {} exprinorder = [] for truth in product((1, 0), repeat=len(irel)): reps = dict(zip(irel, truth)) # only store the true conditions since the false are implied # when they appear lower in the Piecewise args if 1 not in truth: cond = None # flag this one so it doesn't get combined else: andargs = Tuple(*[i for i in reps if reps[i]]) free = list(andargs.free_symbols) if len(free) == 1: from sympy.solvers.inequalities import ( reduce_inequalities, _solve_inequality) try: t = reduce_inequalities(andargs, free[0]) # ValueError when there are potentially # nonvanishing imaginary parts except (ValueError, NotImplementedError): # at least isolate free symbol on left t = And(*[_solve_inequality( a, free[0], linear=True) for a in andargs]) else: t = And(*andargs) if t is S.false: continue # an impossible combination cond = t expr = handler(self.xreplace(reps)) if isinstance(expr, self.func) and len(expr.args) == 1: expr, econd = expr.args[0] cond = And(econd, True if cond is None else cond) # the ec pairs are being collected since all possibilities # are being enumerated, but don't put the last one in since # its expr might match a previous expression and it # must appear last in the args if cond is not None: args.setdefault(expr, []).append(cond) # but since we only store the true conditions we must maintain # the order so that the expression with the most true values # comes first exprinorder.append(expr) # convert collected conditions as args of Or for k in args: args[k] = Or(*args[k]) # take them in the order obtained args = [(e, args[e]) for e in uniq(exprinorder)] # add in the last arg args.append((expr, True)) # if any condition reduced to True, it needs to go last # and there should only be one of them or else the exprs # should agree trues = [i for i in range(len(args)) if args[i][1] is S.true] if not trues: # make the last one True since all cases were enumerated e, c = args[-1] args[-1] = (e, S.true) else: assert len(set([e for e, c in [args[i] for i in trues]])) == 1 args.append(args.pop(trues.pop())) while trues: args.pop(trues.pop()) return Piecewise(*args)
def __new__(cls, expr, *args, **kwargs): expr = sympify(expr) if not args: if expr.is_Order: variables = expr.variables point = expr.point else: variables = list(expr.free_symbols) point = [S.Zero]*len(variables) else: args = list(args if is_sequence(args) else [args]) variables, point = [], [] if is_sequence(args[0]): for a in args: v, p = list(map(sympify, a)) variables.append(v) point.append(p) else: variables = list(map(sympify, args)) point = [S.Zero]*len(variables) if not all(isinstance(v, Symbol) for v in variables): raise TypeError('Variables are not symbols, got %s' % variables) if len(list(uniq(variables))) != len(variables): raise ValueError('Variables are supposed to be unique symbols, got %s' % variables) if expr.is_Order: expr_vp = dict(expr.args[1:]) new_vp = dict(expr_vp) vp = dict(zip(variables, point)) for v, p in vp.items(): if v in new_vp.keys(): if p != new_vp[v]: raise NotImplementedError( "Mixing Order at different points is not supported.") else: new_vp[v] = p if set(expr_vp.keys()) == set(new_vp.keys()): return expr else: variables = list(new_vp.keys()) point = [new_vp[v] for v in variables] if expr is S.NaN: return S.NaN if not all(p is S.Zero for p in point) and \ not all(p is S.Infinity for p in point): raise NotImplementedError('Order at points other than 0 ' 'or oo not supported, got %s as a point.' % point) if variables: if len(variables) > 1: # XXX: better way? We need this expand() to # workaround e.g: expr = x*(x + y). # (x*(x + y)).as_leading_term(x, y) currently returns # x*y (wrong order term!). That's why we want to deal with # expand()'ed expr (handled in "if expr.is_Add" branch below). expr = expr.expand() if expr.is_Add: lst = expr.extract_leading_order(variables, point) expr = Add(*[f.expr for (e, f) in lst]) elif expr: if point[0] == S.Zero: expr = expr.as_leading_term(*variables) expr = expr.as_independent(*variables, as_Add=False)[1] expr = expand_power_base(expr) expr = expand_log(expr) if len(variables) == 1: # The definition of O(f(x)) symbol explicitly stated that # the argument of f(x) is irrelevant. That's why we can # combine some power exponents (only "on top" of the # expression tree for f(x)), e.g.: # x**p * (-x)**q -> x**(p+q) for real p, q. x = variables[0] margs = list(Mul.make_args( expr.as_independent(x, as_Add=False)[1])) for i, t in enumerate(margs): if t.is_Pow: b, q = t.args if b in (x, -x) and q.is_real and not q.has(x): margs[i] = x**q elif b.is_Pow and not b.exp.has(x): b, r = b.args if b in (x, -x) and r.is_real: margs[i] = x**(r*q) elif b.is_Mul and b.args[0] is S.NegativeOne: b = -b if b.is_Pow and not b.exp.has(x): b, r = b.args if b in (x, -x) and r.is_real: margs[i] = x**(r*q) expr = Mul(*margs) if expr is S.Zero: return expr if expr.is_Order: expr = expr.expr if not expr.has(*variables): expr = S.One # create Order instance: variables.sort(key=default_sort_key) args = (expr,) + Tuple(*zip(variables, point)) obj = Expr.__new__(cls, *args) return obj
def encipher_vigenere(pt, key, symbols="ABCDEFGHIJKLMNOPQRSTUVWXYZ"): """ Performs the Vigenère cipher encryption on plaintext ``pt``, and returns the ciphertext. Notes ===== The Vigenère cipher is named after Blaise de Vigenère, a sixteenth century diplomat and cryptographer, by a historical accident. Vigenère actually invented a different and more complicated cipher. The so-called *Vigenère cipher* was actually invented by Giovan Batista Belaso in 1553. This cipher was used in the 1800's, for example, during the American Civil War. The Confederacy used a brass cipher disk to implement the Vigenère cipher (now on display in the NSA Museum in Fort Meade) [1]_. The Vigenère cipher is a generalization of the shift cipher. Whereas the shift cipher shifts each letter by the same amount (that amount being the key of the shift cipher) the Vigenère cipher shifts a letter by an amount determined by the key (which is a word or phrase known only to the sender and receiver). For example, if the key was a single letter, such as "C", then the so-called Vigenere cipher is actually a shift cipher with a shift of `2` (since "C" is the 2nd letter of the alphabet, if you start counting at `0`). If the key was a word with two letters, such as "CA", then the so-called Vigenère cipher will shift letters in even positions by `2` and letters in odd positions are left alone (shifted by `0`, since "A" is the 0th letter, if you start counting at `0`). ALGORITHM: INPUT: ``key``: a string of upper-case letters (the secret key) ``m``: string of upper-case letters (the plaintext message) OUTPUT: ``c``: string of upper-case letters (the ciphertext message) STEPS: 0. Identify the alphabet A, ..., Z with the integers 0, ..., 25. 1. Compute from the string ``key`` a list ``L1`` of corresponding integers. Let ``n1 = len(L1)``. 2. Compute from the string ``m`` a list ``L2`` of corresponding integers. Let ``n2 = len(L2)``. 3. Break ``L2`` up sequencially into sublists of size ``n1``, and one sublist at the end of size smaller or equal to ``n1``. 4. For each of these sublists ``L`` of ``L2``, compute a new list ``C`` given by ``C[i] = L[i] + L1[i] (mod 26)`` to the ``i``-th element in the sublist, for each ``i``. 5. Assemble these lists ``C`` by concatenation into a new list of length ``n2``. 6. Compute from the new list a string ``c`` of corresponding letters. Once it is known that the key is, say, `n` characters long, frequency analysis can be applied to every `n`-th letter of the ciphertext to determine the plaintext. This method is called *Kasiski examination* (although it was first discovered by Babbage). The cipher Vigenère actually discovered is an "auto-key" cipher described as follows. ALGORITHM: INPUT: ``key``: a string of upper-case letters (the secret key) ``m``: string of upper-case letters (the plaintext message) OUTPUT: ``c``: string of upper-case letters (the ciphertext message) STEPS: 0. Identify the alphabet A, ..., Z with the integers 0, ..., 25. 1. Compute from the string ``m`` a list ``L2`` of corresponding integers. Let ``n2 = len(L2)``. 2. Let ``n1`` be the length of the key. Concatenate the string ``key`` with the first ``n2 - n1`` characters of the plaintext message. Compute from this string of length ``n2`` a list ``L1`` of corresponding integers. Note ``n2 = len(L1)``. 3. Compute a new list ``C`` given by ``C[i] = L1[i] + L2[i] (mod 26)``, for each ``i``. Note ``n2 = len(C)``. 4. Compute from the new list a string ``c`` of corresponding letters. References ========== .. [1] http://en.wikipedia.org/wiki/Vigenere_cipher Examples ======== >>> from sympy.crypto.crypto import encipher_vigenere >>> key = "encrypt" >>> pt = "meet me on monday" >>> encipher_vigenere(pt, key) 'QRGKKTHRZQEBPR' """ symbols = "".join(symbols) A = alphabet_of_cipher(symbols) N = len(A) # normally, 26 key0 = uniq(key) key0 = [x.capitalize() for x in key0 if x.isalnum()] K = [A.index(x) for x in key0] k = len(K) pt0 = [x.capitalize() for x in pt if x.isalnum()] P = [A.index(x) for x in pt0] n = len(P) #m = n//k C = [(K[i % k] + P[i]) % N for i in range(n)] return "".join([str(A[x]) for x in C])
def encipher_vigenere(pt, key, symbols="ABCDEFGHIJKLMNOPQRSTUVWXYZ"): """ Performs the Vigenere cipher encryption on plaintext ``pt``, and returns the ciphertext. Notes ===== The Vigenere cipher is named after Blaise de Vigenere, a sixteenth century diplomat and cryptographer, by a historical accident. Vigene`re actually invented a different and more complicated cipher. The so-called *Vigenere cipher* cipher was actually invented by Giovan Batista Belaso in 1553. This cipher was used in the 1700's, for example, during the American Civil War. The Confederacy used a brass cipher disk to implement the Vigenere cipher (now on display in the NSA Museum in Fort Meade) [1]_. The Vigenere cipher is a generalization of the shift cipher. Whereas the shift cipher shifts each letter by the same amount (that amount being the key of the shift cipher) the Vigenere cipher shifts a letter by an amount determined by the key, which is a word or phrase known only to the sender and receiver). For example, if the key was a single letter, such as "C", then the so-called Vigenere cipher is actually a shift cipher with a shift of `2` (since "C" is the 2nd letter of the alphabet, if you start counting at `0`). If the key was a word with two letters, such as "CA", then the so-called Vigenere cipher will shift letters in even positions by `2` and letters in odd positions are left alone (shifted by `0`, since "A" is the 0th letter, if you start counting at `0`). ALGORITHM: INPUT: ``key``: a string of upper-case letters (the secret key) ``m``: string of upper-case letters (the plaintext message) OUTPUT: ``c``: string of upper-case letters (the ciphertext message) STEPS: 0. Identify the alphabet A, ..., Z with the integers 0, ..., 25. 1. Compute from the string ``key`` a list ``L1`` of corresponding integers. Let ``n1 = len(L1)``. 2. Compute from the string ``m`` a list ``L2`` of corresponding integers. Let ``n2 = len(L2)``. 3. Break ``L2`` up sequencially into sublists of size ``n1``, and one sublist at the end of size smaller or equal to ``n1``. 4. For each of these sublists ``L`` of ``L2``, compute a new list ``C`` given by ``C[i] = L[i] + L1[i] (mod 26)`` to the ``i``-th element in the sublist, for each ``i``. 5. Assemble these lists ``C`` by concatenation into a new list of length ``n2``. 6. Compute from the new list a string ``c`` of corresponding letters. Once it is known that the key is, say, `n` characters long, frequency analysis can be applied to every `n`-th letter of the ciphertext to determine the plaintext. This method is called *Kasiski examination* (although it was first discovered by Babbage). The cipher Vigenere actually discovered is an "auto-key" cipher described as follows. ALGORITHM: INPUT: ``key``: a string of upper-case letters (the secret key) ``m``: string of upper-case letters (the plaintext message) OUTPUT: ``c``: string of upper-case letters (the ciphertext message) STEPS: 0. Identify the alphabet A, ..., Z with the integers 0, ..., 25. 1. Compute from the string ``m`` a list ``L2`` of corresponding integers. Let ``n2 = len(L2)``. 2. Let ``n1`` be the length of the key. Concatenate the string ``key`` with the first ``n2 - n1`` characters of the plaintext message. Compute from this string of length ``n2`` a list ``L1`` of corresponding integers. Note ``n2 = len(L1)``. 3. Compute a new list ``C`` given by ``C[i] = L1[i] + L2[i] (mod 26)``, for each ``i``. Note ``n2 = len(C)``. 4. Compute from the new list a string ``c`` of corresponding letters. References ========== .. [1] http://en.wikipedia.org/wiki/Vigenere_cipher Examples ======== >>> from sympy.crypto.crypto import encipher_vigenere >>> key = "encrypt" >>> pt = "meet me on monday" >>> encipher_vigenere(pt, key) 'QRGKKTHRZQEBPR' """ symbols = "".join(symbols) A = alphabet_of_cipher(symbols) N = len(A) # normally, 26 key0 = uniq(key) key0 = [x.capitalize() for x in key0 if x.isalnum()] K = [A.index(x) for x in key0] k = len(K) pt0 = [x.capitalize() for x in pt if x.isalnum()] P = [A.index(x) for x in pt0] n = len(P) #m = n//k C = [(K[i % k] + P[i]) % N for i in range(n)] return "".join([str(A[x]) for x in C])
def test_uniq(): assert list(uniq(p.copy() for p in partitions(4))) == [{4: 1}, {1: 1, 3: 1}, {2: 2}, {1: 2, 2: 1}, {1: 4}] assert list(uniq(x % 2 for x in range(5))) == [0, 1] assert list(uniq("a")) == ["a"] assert list(uniq("ababc")) == list("abc")
def encipher_bifid5(pt, key): r""" Performs the Bifid cipher encryption on plaintext ``pt``, and returns the ciphertext. This is the version of the Bifid cipher that uses the `5 \times 5` Polybius square. Notes ===== The Bifid cipher was invented around 1901 by Felix Delastelle. It is a *fractional substitution* cipher, where letters are replaced by pairs of symbols from a smaller alphabet. The cipher uses a `5 \times 5` square filled with some ordering of the alphabet, except that "i"s and "j"s are identified (this is a so-called Polybius square; there is a `6 \times 6` analog if you add back in "j" and also append onto the usual 26 letter alphabet, the digits 0, 1, ..., 9). According to Helen Gaines' book *Cryptanalysis*, this type of cipher was used in the field by the German Army during World War I. ALGORITHM: (5x5 case) INPUT: ``pt``: plaintext string (no "j"s) ``key``: short string for key (no repetitions, no "j"s) OUTPUT: ciphertext (using Bifid5 cipher in all caps, no spaces, no "J"s) STEPS: 1. Create the `5 \times 5` Polybius square ``S`` associated to the ``k`` as follows: a) starting top left, moving left-to-right, top-to-bottom, place the letters of the key into a 5x5 matrix, b) when finished, add the letters of the alphabet not in the key until the 5x5 square is filled 2. Create a list ``P`` of pairs of numbers which are the coordinates in the Polybius square of the letters in ``pt``. 3. Let ``L1`` be the list of all first coordinates of ``P`` (length of ``L1 = n``), let ``L2`` be the list of all second coordinates of ``P`` (so the length of ``L2`` is also ``n``). 4. Let ``L`` be the concatenation of ``L1`` and ``L2`` (length ``L = 2*n``), except that consecutive numbers are paired ``(L[2*i], L[2*i + 1])``. You can regard ``L`` as a list of pairs of length ``n``. 5. Let ``C`` be the list of all letters which are of the form ``S[i, j]``, for all ``(i, j)`` in ``L``. As a string, this is the ciphertext ``ct``. Examples ======== >>> from sympy.crypto.crypto import encipher_bifid5 >>> pt = "meet me on monday" >>> key = "encrypt" >>> encipher_bifid5(pt, key) 'LNLLQNPPNPGADK' >>> pt = "meet me on friday" >>> encipher_bifid5(pt, key) 'LNLLFGPPNPGRSK' """ A = alphabet_of_cipher() # first make sure the letters are capitalized # and text has no spaces key = uniq(key) key0 = [x.capitalize() for x in key if x.isalnum()] pt0 = [x.capitalize() for x in pt if x.isalnum()] # create long key long_key = key0 + [x for x in A if (not (x in key0) and x != "J")] n = len(pt0) # the fractionalization pairs = [[long_key.index(x) // 5, long_key.index(x) % 5] for x in pt0] tmp_cipher = flatten([x[0] for x in pairs] + [x[1] for x in pairs]) ct = "".join([ long_key[5 * tmp_cipher[2 * i] + tmp_cipher[2 * i + 1]] for i in range(n) ]) return ct
def heurisch_wrapper(f, x, rewrite=False, hints=None, mappings=None, retries=3, degree_offset=0, unnecessary_permutations=None, _try_heurisch=None): """ A wrapper around the heurisch integration algorithm. Explanation =========== This method takes the result from heurisch and checks for poles in the denominator. For each of these poles, the integral is reevaluated, and the final integration result is given in terms of a Piecewise. Examples ======== >>> from sympy import cos, symbols >>> from sympy.integrals.heurisch import heurisch, heurisch_wrapper >>> n, x = symbols('n x') >>> heurisch(cos(n*x), x) sin(n*x)/n >>> heurisch_wrapper(cos(n*x), x) Piecewise((sin(n*x)/n, Ne(n, 0)), (x, True)) See Also ======== heurisch """ from sympy.solvers.solvers import solve, denoms f = sympify(f) if not f.has_free(x): return f*x res = heurisch(f, x, rewrite, hints, mappings, retries, degree_offset, unnecessary_permutations, _try_heurisch) if not isinstance(res, Basic): return res # We consider each denominator in the expression, and try to find # cases where one or more symbolic denominator might be zero. The # conditions for these cases are stored in the list slns. slns = [] for d in denoms(res): try: slns += solve(d, dict=True, exclude=(x,)) except NotImplementedError: pass if not slns: return res slns = list(uniq(slns)) # Remove the solutions corresponding to poles in the original expression. slns0 = [] for d in denoms(f): try: slns0 += solve(d, dict=True, exclude=(x,)) except NotImplementedError: pass slns = [s for s in slns if s not in slns0] if not slns: return res if len(slns) > 1: eqs = [] for sub_dict in slns: eqs.extend([Eq(key, value) for key, value in sub_dict.items()]) slns = solve(eqs, dict=True, exclude=(x,)) + slns # For each case listed in the list slns, we reevaluate the integral. pairs = [] for sub_dict in slns: expr = heurisch(f.subs(sub_dict), x, rewrite, hints, mappings, retries, degree_offset, unnecessary_permutations, _try_heurisch) cond = And(*[Eq(key, value) for key, value in sub_dict.items()]) generic = Or(*[Ne(key, value) for key, value in sub_dict.items()]) if expr is None: expr = integrate(f.subs(sub_dict),x) pairs.append((expr, cond)) # If there is one condition, put the generic case first. Otherwise, # doing so may lead to longer Piecewise formulas if len(pairs) == 1: pairs = [(heurisch(f, x, rewrite, hints, mappings, retries, degree_offset, unnecessary_permutations, _try_heurisch), generic), (pairs[0][0], True)] else: pairs.append((heurisch(f, x, rewrite, hints, mappings, retries, degree_offset, unnecessary_permutations, _try_heurisch), True)) return Piecewise(*pairs)
def ordered(seq, keys=None, default=True, warn=False): """Return an iterator of the seq where keys are used to break ties in a conservative fashion: if, after applying a key, there are no ties then no other keys will be computed. Two default keys will be applied if 1) keys are not provided or 2) the given keys don't resolve all ties (but only if `default` is True). The two keys are `_nodes` (which places smaller expressions before large) and `default_sort_key` which (if the `sort_key` for an object is defined properly) should resolve any ties. If ``warn`` is True then an error will be raised if there were no keys remaining to break ties. This can be used if it was expected that there should be no ties between items that are not identical. Examples ======== >>> from sympy.utilities.iterables import ordered >>> from sympy import count_ops >>> from sympy.abc import x, y The count_ops is not sufficient to break ties in this list and the first two items appear in their original order (i.e. the sorting is stable): >>> list(ordered([y + 2, x + 2, x**2 + y + 3], ... count_ops, default=False, warn=False)) ... [y + 2, x + 2, x**2 + y + 3] The default_sort_key allows the tie to be broken: >>> list(ordered([y + 2, x + 2, x**2 + y + 3])) ... [x + 2, y + 2, x**2 + y + 3] Here, sequences are sorted by length, then sum: >>> seq, keys = [[[1, 2, 1], [0, 3, 1], [1, 1, 3], [2], [1]], [ ... lambda x: len(x), ... lambda x: sum(x)]] ... >>> list(ordered(seq, keys, default=False, warn=False)) [[1], [2], [1, 2, 1], [0, 3, 1], [1, 1, 3]] If ``warn`` is True, an error will be raised if there were not enough keys to break ties: >>> list(ordered(seq, keys, default=False, warn=True)) Traceback (most recent call last): ... ValueError: not enough keys to break ties Notes ===== The decorated sort is one of the fastest ways to sort a sequence for which special item comparison is desired: the sequence is decorated, sorted on the basis of the decoration (e.g. making all letters lower case) and then undecorated. If one wants to break ties for items that have the same decorated value, a second key can be used. But if the second key is expensive to compute then it is inefficient to decorate all items with both keys: only those items having identical first key values need to be decorated. This function applies keys successively only when needed to break ties. By yielding an iterator, use of the tie-breaker is delayed as long as possible. This function is best used in cases when use of the first key is expected to be a good hashing function; if there are no unique hashes from application of a key then that key should not have been used. The exception, however, is that even if there are many collisions, if the first group is small and one does not need to process all items in the list then time will not be wasted sorting what one was not interested in. For example, if one were looking for the minimum in a list and there were several criteria used to define the sort order, then this function would be good at returning that quickly if the first group of candidates is small relative to the number of items being processed. """ d = defaultdict(list) if keys: if not isinstance(keys, (list, tuple)): keys = [keys] keys = list(keys) f = keys.pop(0) for a in seq: d[f(a)].append(a) else: if not default: raise ValueError('if default=False then keys must be provided') d[None].extend(seq) for k in sorted(d.keys()): if len(d[k]) > 1: if keys: d[k] = ordered(d[k], keys, default, warn) elif default: d[k] = ordered(d[k], ( _nodes, default_sort_key, ), default=False, warn=warn) elif warn: from sympy.utilities.iterables import uniq u = list(uniq(d[k])) if len(u) > 1: raise ValueError('not enough keys to break ties: %s' % u) for v in d[k]: yield v d.pop(k)
def _intervals(self, sym): """Return a list of unique tuples, (a, b, e, i), where a and b are the lower and upper bounds in which the expression e of argument i in self is defined and a < b (when involving numbers) or a <= b when involving symbols. If there are any relationals not involving sym, or any relational cannot be solved for sym, NotImplementedError is raised. The calling routine should have removed such relationals before calling this routine. The evaluated conditions will be returned as ranges. Discontinuous ranges will be returned separately with identical expressions. The first condition that evaluates to True will be returned as the last tuple with a, b = -oo, oo. """ from sympy.solvers.inequalities import _solve_inequality from sympy.logic.boolalg import to_cnf, distribute_or_over_and assert isinstance(self, Piecewise) def _solve_relational(r): if sym not in r.free_symbols: nonsymfail(r) rv = _solve_inequality(r, sym) if isinstance(rv, Relational): free = rv.args[1].free_symbols if rv.args[0] != sym or sym in free: raise NotImplementedError(filldedent(''' Unable to solve relational %s for %s.''' % (r, sym))) if rv.rel_op == '==': # this equality has been affirmed to have the form # Eq(sym, rhs) where rhs is sym-free; it represents # a zero-width interval which will be ignored # whether it is an isolated condition or contained # within an And or an Or rv = S.false elif rv.rel_op == '!=': try: rv = Or(sym < rv.rhs, sym > rv.rhs) except TypeError: # e.g. x != I ==> all real x satisfy rv = S.true elif rv == (S.NegativeInfinity < sym) & (sym < S.Infinity): rv = S.true return rv def nonsymfail(cond): raise NotImplementedError(filldedent(''' A condition not involving %s appeared: %s''' % (sym, cond))) # make self canonical wrt Relationals reps = dict([ (r, _solve_relational(r)) for r in self.atoms(Relational)]) # process args individually so if any evaluate, their position # in the original Piecewise will be known args = [i.xreplace(reps) for i in self.args] # precondition args expr_cond = [] default = idefault = None for i, (expr, cond) in enumerate(args): if cond is S.false: continue elif cond is S.true: default = expr idefault = i break cond = to_cnf(cond) if isinstance(cond, And): cond = distribute_or_over_and(cond) if isinstance(cond, Or): expr_cond.extend( [(i, expr, o) for o in cond.args if not isinstance(o, Equality)]) elif cond is not S.false: expr_cond.append((i, expr, cond)) # determine intervals represented by conditions int_expr = [] for iarg, expr, cond in expr_cond: if isinstance(cond, And): lower = S.NegativeInfinity upper = S.Infinity for cond2 in cond.args: if isinstance(cond2, Equality): lower = upper # ignore break elif cond2.lts == sym: upper = Min(cond2.gts, upper) elif cond2.gts == sym: lower = Max(cond2.lts, lower) else: nonsymfail(cond2) # should never get here elif isinstance(cond, Relational): lower, upper = cond.lts, cond.gts # part 1: initialize with givens if cond.lts == sym: # part 1a: expand the side ... lower = S.NegativeInfinity # e.g. x <= 0 ---> -oo <= 0 elif cond.gts == sym: # part 1a: ... that can be expanded upper = S.Infinity # e.g. x >= 0 ---> oo >= 0 else: nonsymfail(cond) else: raise NotImplementedError( 'unrecognized condition: %s' % cond) lower, upper = lower, Max(lower, upper) if (lower >= upper) is not S.true: int_expr.append((lower, upper, expr, iarg)) if default is not None: int_expr.append( (S.NegativeInfinity, S.Infinity, default, idefault)) return list(uniq(int_expr))
def ordered(seq, keys=None, default=True, warn=False): """Return an iterator of the seq where keys are used to break ties in a conservative fashion: if, after applying a key, there are no ties then no other keys will be computed. Two default keys will be applied if 1) keys are not provided or 2) the given keys don't resolve all ties (but only if `default` is True). The two keys are `_nodes` (which places smaller expressions before large) and `default_sort_key` which (if the `sort_key` for an object is defined properly) should resolve any ties. If ``warn`` is True then an error will be raised if there were no keys remaining to break ties. This can be used if it was expected that there should be no ties between items that are not identical. Examples ======== >>> from sympy.utilities.iterables import ordered >>> from sympy import count_ops >>> from sympy.abc import x, y The count_ops is not sufficient to break ties in this list and the first two items appear in their original order (i.e. the sorting is stable): >>> list(ordered([y + 2, x + 2, x**2 + y + 3], ... count_ops, default=False, warn=False)) ... [y + 2, x + 2, x**2 + y + 3] The default_sort_key allows the tie to be broken: >>> list(ordered([y + 2, x + 2, x**2 + y + 3])) ... [x + 2, y + 2, x**2 + y + 3] Here, sequences are sorted by length, then sum: >>> seq, keys = [[[1, 2, 1], [0, 3, 1], [1, 1, 3], [2], [1]], [ ... lambda x: len(x), ... lambda x: sum(x)]] ... >>> list(ordered(seq, keys, default=False, warn=False)) [[1], [2], [1, 2, 1], [0, 3, 1], [1, 1, 3]] If ``warn`` is True, an error will be raised if there were not enough keys to break ties: >>> list(ordered(seq, keys, default=False, warn=True)) Traceback (most recent call last): ... ValueError: not enough keys to break ties Notes ===== The decorated sort is one of the fastest ways to sort a sequence for which special item comparison is desired: the sequence is decorated, sorted on the basis of the decoration (e.g. making all letters lower case) and then undecorated. If one wants to break ties for items that have the same decorated value, a second key can be used. But if the second key is expensive to compute then it is inefficient to decorate all items with both keys: only those items having identical first key values need to be decorated. This function applies keys successively only when needed to break ties. By yielding an iterator, use of the tie-breaker is delayed as long as possible. This function is best used in cases when use of the first key is expected to be a good hashing function; if there are no unique hashes from application of a key then that key should not have been used. The exception, however, is that even if there are many collisions, if the first group is small and one does not need to process all items in the list then time will not be wasted sorting what one was not interested in. For example, if one were looking for the minimum in a list and there were several criteria used to define the sort order, then this function would be good at returning that quickly if the first group of candidates is small relative to the number of items being processed. """ d = defaultdict(list) if keys: if not isinstance(keys, (list, tuple)): keys = [keys] keys = list(keys) f = keys.pop(0) for a in seq: d[f(a)].append(a) else: if not default: raise ValueError('if default=False then keys must be provided') d[None].extend(seq) for k in sorted(d.keys()): if len(d[k]) > 1: if keys: d[k] = ordered(d[k], keys, default, warn) elif default: d[k] = ordered(d[k], (_nodes, default_sort_key,), default=False, warn=warn) elif warn: from sympy.utilities.iterables import uniq u = list(uniq(d[k])) if len(u) > 1: raise ValueError( 'not enough keys to break ties: %s' % u) for v in d[k]: yield v d.pop(k)
def __new__(cls, expr, *args, **kwargs): expr = sympify(expr) if not args: if expr.is_Order: variables = expr.variables point = expr.point else: variables = list(expr.free_symbols) point = [S.Zero] * len(variables) else: args = list(args if is_sequence(args) else [args]) variables, point = [], [] if is_sequence(args[0]): for a in args: v, p = list(map(sympify, a)) variables.append(v) point.append(p) else: variables = list(map(sympify, args)) point = [S.Zero] * len(variables) if not all(v.is_symbol for v in variables): raise TypeError('Variables are not symbols, got %s' % variables) if len(list(uniq(variables))) != len(variables): raise ValueError( 'Variables are supposed to be unique symbols, got %s' % variables) if expr.is_Order: expr_vp = dict(expr.args[1:]) new_vp = dict(expr_vp) vp = dict(zip(variables, point)) for v, p in vp.items(): if v in new_vp.keys(): if p != new_vp[v]: raise NotImplementedError( "Mixing Order at different points is not supported." ) else: new_vp[v] = p if set(expr_vp.keys()) == set(new_vp.keys()): return expr else: variables = list(new_vp.keys()) point = [new_vp[v] for v in variables] if expr is S.NaN: return S.NaN if any(x in p.free_symbols for x in variables for p in point): raise ValueError('Got %s as a point.' % point) if variables: if any(p != point[0] for p in point): raise NotImplementedError( "Multivariable orders at different points are not supported." ) if point[0] is S.Infinity: s = {k: 1 / Dummy() for k in variables} rs = {1 / v: 1 / k for k, v in s.items()} elif point[0] is S.NegativeInfinity: s = {k: -1 / Dummy() for k in variables} rs = {-1 / v: -1 / k for k, v in s.items()} elif point[0] is not S.Zero: s = dict((k, Dummy() + point[0]) for k in variables) rs = dict((v - point[0], k - point[0]) for k, v in s.items()) else: s = () rs = () expr = expr.subs(s) if expr.is_Add: from sympy import expand_multinomial expr = expand_multinomial(expr) if s: args = tuple([r[0] for r in rs.items()]) else: args = tuple(variables) if len(variables) > 1: # XXX: better way? We need this expand() to # workaround e.g: expr = x*(x + y). # (x*(x + y)).as_leading_term(x, y) currently returns # x*y (wrong order term!). That's why we want to deal with # expand()'ed expr (handled in "if expr.is_Add" branch below). expr = expr.expand() if expr.is_Add: lst = expr.extract_leading_order(args) expr = Add(*[f.expr for (e, f) in lst]) elif expr: expr = expr.as_leading_term(*args) expr = expr.as_independent(*args, as_Add=False)[1] expr = expand_power_base(expr) expr = expand_log(expr) if len(args) == 1: # The definition of O(f(x)) symbol explicitly stated that # the argument of f(x) is irrelevant. That's why we can # combine some power exponents (only "on top" of the # expression tree for f(x)), e.g.: # x**p * (-x)**q -> x**(p+q) for real p, q. x = args[0] margs = list( Mul.make_args(expr.as_independent(x, as_Add=False)[1])) for i, t in enumerate(margs): if t.is_Power: b, q = t.args if b in (x, -x) and q.is_real and not q.has(x): margs[i] = x**q elif b.is_Power and not b.exp.has(x): b, r = b.args if b in (x, -x) and r.is_real: margs[i] = x**(r * q) elif b.is_Mul and b.args[0] is S.NegativeOne: b = -b if b.is_Power and not b.exp.has(x): b, r = b.args if b in (x, -x) and r.is_real: margs[i] = x**(r * q) expr = Mul(*margs) expr = expr.subs(rs) if expr is S.Zero: return expr if expr.is_Order: expr = expr.expr if not expr.has(*variables): expr = S.One # create Order instance: vp = dict(zip(variables, point)) variables.sort(key=default_sort_key) point = [vp[v] for v in variables] args = (expr, ) + Tuple(*zip(variables, point)) obj = Expr.__new__(cls, *args) return obj
def test_uniq(): assert list(uniq(p.copy() for p in partitions(4))) == \ [{4: 1}, {1: 1, 3: 1}, {2: 2}, {1: 2, 2: 1}, {1: 4}] assert list(uniq(x % 2 for x in range(5))) == [0, 1] assert list(uniq('a')) == ['a'] assert list(uniq('ababc')) == list('abc')