1"""
2A Printer for generating readable representation of most sympy classes.
3"""
4
5from typing import Any, Dict
6
7from sympy.core import S, Rational, Pow, Basic, Mul, Number
8from sympy.core.mul import _keep_coeff
9from sympy.core.function import _coeff_isneg
10from sympy.sets.sets import FiniteSet
11from .printer import Printer, print_function
12from sympy.printing.precedence import precedence, PRECEDENCE
13
14from mpmath.libmp import prec_to_dps, to_str as mlib_to_str
15
16from sympy.utilities import default_sort_key, sift
17
18
19class StrPrinter(Printer):
20    printmethod = "_sympystr"
21    _default_settings = {
22        "order": None,
23        "full_prec": "auto",
24        "sympy_integers": False,
25        "abbrev": False,
26        "perm_cyclic": True,
27        "min": None,
28        "max": None,
29    }  # type: Dict[str, Any]
30
31    _relationals = dict()  # type: Dict[str, str]
32
33    def parenthesize(self, item, level, strict=False):
34        if (precedence(item) < level) or ((not strict) and precedence(item) <= level):
35            return "(%s)" % self._print(item)
36        else:
37            return self._print(item)
38
39    def stringify(self, args, sep, level=0):
40        return sep.join([self.parenthesize(item, level) for item in args])
41
42    def emptyPrinter(self, expr):
43        if isinstance(expr, str):
44            return expr
45        elif isinstance(expr, Basic):
46            return repr(expr)
47        else:
48            return str(expr)
49
50    def _print_Add(self, expr, order=None):
51        terms = self._as_ordered_terms(expr, order=order)
52
53        PREC = precedence(expr)
54        l = []
55        for term in terms:
56            t = self._print(term)
57            if t.startswith('-'):
58                sign = "-"
59                t = t[1:]
60            else:
61                sign = "+"
62            if precedence(term) < PREC:
63                l.extend([sign, "(%s)" % t])
64            else:
65                l.extend([sign, t])
66        sign = l.pop(0)
67        if sign == '+':
68            sign = ""
69        return sign + ' '.join(l)
70
71    def _print_BooleanTrue(self, expr):
72        return "True"
73
74    def _print_BooleanFalse(self, expr):
75        return "False"
76
77    def _print_Not(self, expr):
78        return '~%s' %(self.parenthesize(expr.args[0],PRECEDENCE["Not"]))
79
80    def _print_And(self, expr):
81        return self.stringify(expr.args, " & ", PRECEDENCE["BitwiseAnd"])
82
83    def _print_Or(self, expr):
84        return self.stringify(expr.args, " | ", PRECEDENCE["BitwiseOr"])
85
86    def _print_Xor(self, expr):
87        return self.stringify(expr.args, " ^ ", PRECEDENCE["BitwiseXor"])
88
89    def _print_AppliedPredicate(self, expr):
90        return '%s(%s)' % (
91            self._print(expr.function), self.stringify(expr.arguments, ", "))
92
93    def _print_Basic(self, expr):
94        l = [self._print(o) for o in expr.args]
95        return expr.__class__.__name__ + "(%s)" % ", ".join(l)
96
97    def _print_BlockMatrix(self, B):
98        if B.blocks.shape == (1, 1):
99            self._print(B.blocks[0, 0])
100        return self._print(B.blocks)
101
102    def _print_Catalan(self, expr):
103        return 'Catalan'
104
105    def _print_ComplexInfinity(self, expr):
106        return 'zoo'
107
108    def _print_ConditionSet(self, s):
109        args = tuple([self._print(i) for i in (s.sym, s.condition)])
110        if s.base_set is S.UniversalSet:
111            return 'ConditionSet(%s, %s)' % args
112        args += (self._print(s.base_set),)
113        return 'ConditionSet(%s, %s, %s)' % args
114
115    def _print_Derivative(self, expr):
116        dexpr = expr.expr
117        dvars = [i[0] if i[1] == 1 else i for i in expr.variable_count]
118        return 'Derivative(%s)' % ", ".join(map(lambda arg: self._print(arg), [dexpr] + dvars))
119
120    def _print_dict(self, d):
121        keys = sorted(d.keys(), key=default_sort_key)
122        items = []
123
124        for key in keys:
125            item = "%s: %s" % (self._print(key), self._print(d[key]))
126            items.append(item)
127
128        return "{%s}" % ", ".join(items)
129
130    def _print_Dict(self, expr):
131        return self._print_dict(expr)
132
133    def _print_RandomDomain(self, d):
134        if hasattr(d, 'as_boolean'):
135            return 'Domain: ' + self._print(d.as_boolean())
136        elif hasattr(d, 'set'):
137            return ('Domain: ' + self._print(d.symbols) + ' in ' +
138                    self._print(d.set))
139        else:
140            return 'Domain on ' + self._print(d.symbols)
141
142    def _print_Dummy(self, expr):
143        return '_' + expr.name
144
145    def _print_EulerGamma(self, expr):
146        return 'EulerGamma'
147
148    def _print_Exp1(self, expr):
149        return 'E'
150
151    def _print_ExprCondPair(self, expr):
152        return '(%s, %s)' % (self._print(expr.expr), self._print(expr.cond))
153
154    def _print_Function(self, expr):
155        return expr.func.__name__ + "(%s)" % self.stringify(expr.args, ", ")
156
157    def _print_GoldenRatio(self, expr):
158        return 'GoldenRatio'
159
160    def _print_Heaviside(self, expr):
161        # Same as _print_Function but uses pargs to suppress default 1/2 for
162        # 2nd args
163        return expr.func.__name__ + "(%s)" % self.stringify(expr.pargs, ", ")
164
165    def _print_TribonacciConstant(self, expr):
166        return 'TribonacciConstant'
167
168    def _print_ImaginaryUnit(self, expr):
169        return 'I'
170
171    def _print_Infinity(self, expr):
172        return 'oo'
173
174    def _print_Integral(self, expr):
175        def _xab_tostr(xab):
176            if len(xab) == 1:
177                return self._print(xab[0])
178            else:
179                return self._print((xab[0],) + tuple(xab[1:]))
180        L = ', '.join([_xab_tostr(l) for l in expr.limits])
181        return 'Integral(%s, %s)' % (self._print(expr.function), L)
182
183    def _print_Interval(self, i):
184        fin =  'Interval{m}({a}, {b})'
185        a, b, l, r = i.args
186        if a.is_infinite and b.is_infinite:
187            m = ''
188        elif a.is_infinite and not r:
189            m = ''
190        elif b.is_infinite and not l:
191            m = ''
192        elif not l and not r:
193            m = ''
194        elif l and r:
195            m = '.open'
196        elif l:
197            m = '.Lopen'
198        else:
199            m = '.Ropen'
200        return fin.format(**{'a': a, 'b': b, 'm': m})
201
202    def _print_AccumulationBounds(self, i):
203        return "AccumBounds(%s, %s)" % (self._print(i.min),
204                                        self._print(i.max))
205
206    def _print_Inverse(self, I):
207        return "%s**(-1)" % self.parenthesize(I.arg, PRECEDENCE["Pow"])
208
209    def _print_Lambda(self, obj):
210        expr = obj.expr
211        sig = obj.signature
212        if len(sig) == 1 and sig[0].is_symbol:
213            sig = sig[0]
214        return "Lambda(%s, %s)" % (self._print(sig), self._print(expr))
215
216    def _print_LatticeOp(self, expr):
217        args = sorted(expr.args, key=default_sort_key)
218        return expr.func.__name__ + "(%s)" % ", ".join(self._print(arg) for arg in args)
219
220    def _print_Limit(self, expr):
221        e, z, z0, dir = expr.args
222        if str(dir) == "+":
223            return "Limit(%s, %s, %s)" % tuple(map(self._print, (e, z, z0)))
224        else:
225            return "Limit(%s, %s, %s, dir='%s')" % tuple(map(self._print,
226                                                            (e, z, z0, dir)))
227
228    def _print_list(self, expr):
229        return "[%s]" % self.stringify(expr, ", ")
230
231    def _print_MatrixBase(self, expr):
232        return expr._format_str(self)
233
234    def _print_MatrixElement(self, expr):
235        return self.parenthesize(expr.parent, PRECEDENCE["Atom"], strict=True) \
236            + '[%s, %s]' % (self._print(expr.i), self._print(expr.j))
237
238    def _print_MatrixSlice(self, expr):
239        def strslice(x, dim):
240            x = list(x)
241            if x[2] == 1:
242                del x[2]
243            if x[0] == 0:
244                x[0] = ''
245            if x[1] == dim:
246                x[1] = ''
247            return ':'.join(map(lambda arg: self._print(arg), x))
248        return (self.parenthesize(expr.parent, PRECEDENCE["Atom"], strict=True) + '[' +
249                strslice(expr.rowslice, expr.parent.rows) + ', ' +
250                strslice(expr.colslice, expr.parent.cols) + ']')
251
252    def _print_DeferredVector(self, expr):
253        return expr.name
254
255    def _print_Mul(self, expr):
256
257        prec = precedence(expr)
258
259        # Check for unevaluated Mul. In this case we need to make sure the
260        # identities are visible, multiple Rational factors are not combined
261        # etc so we display in a straight-forward form that fully preserves all
262        # args and their order.
263        args = expr.args
264        if args[0] is S.One or any(
265                isinstance(a, Number) or
266                a.is_Pow and all(ai.is_Integer for ai in a.args)
267                for a in args[1:]):
268            d, n = sift(args, lambda x:
269                isinstance(x, Pow) and bool(x.exp.as_coeff_Mul()[0] < 0),
270                binary=True)
271            for i, di in enumerate(d):
272                if di.exp.is_Number:
273                    e = -di.exp
274                else:
275                    dargs = list(di.exp.args)
276                    dargs[0] = -dargs[0]
277                    e = Mul._from_args(dargs)
278                d[i] = Pow(di.base, e, evaluate=False) if e - 1 else di.base
279
280            # don't parenthesize first factor if negative
281            if _coeff_isneg(n[0]):
282                pre = [str(n.pop(0))]
283            else:
284                pre = []
285            nfactors = pre + [self.parenthesize(a, prec, strict=False)
286                for a in n]
287
288            # don't parenthesize first of denominator unless singleton
289            if len(d) > 1 and _coeff_isneg(d[0]):
290                pre = [str(d.pop(0))]
291            else:
292                pre = []
293            dfactors = pre + [self.parenthesize(a, prec, strict=False)
294                for a in d]
295
296            n = '*'.join(nfactors)
297            d = '*'.join(dfactors)
298            if len(dfactors) > 1:
299                return '%s/(%s)' % (n, d)
300            elif dfactors:
301                return '%s/%s' % (n, d)
302            return n
303
304        c, e = expr.as_coeff_Mul()
305        if c < 0:
306            expr = _keep_coeff(-c, e)
307            sign = "-"
308        else:
309            sign = ""
310
311        a = []  # items in the numerator
312        b = []  # items that are in the denominator (if any)
313
314        pow_paren = []  # Will collect all pow with more than one base element and exp = -1
315
316        if self.order not in ('old', 'none'):
317            args = expr.as_ordered_factors()
318        else:
319            # use make_args in case expr was something like -x -> x
320            args = Mul.make_args(expr)
321
322        # Gather args for numerator/denominator
323        def apow(i):
324            b, e = i.as_base_exp()
325            eargs = list(Mul.make_args(e))
326            if eargs[0] is S.NegativeOne:
327                eargs = eargs[1:]
328            else:
329                eargs[0] = -eargs[0]
330            e = Mul._from_args(eargs)
331            if isinstance(i, Pow):
332                return i.func(b, e, evaluate=False)
333            return i.func(e, evaluate=False)
334        for item in args:
335            if (item.is_commutative and
336                    isinstance(item, Pow) and
337                    bool(item.exp.as_coeff_Mul()[0] < 0)):
338                if item.exp is not S.NegativeOne:
339                    b.append(apow(item))
340                else:
341                    if (len(item.args[0].args) != 1 and
342                            isinstance(item.base, (Mul, Pow))):
343                        # To avoid situations like #14160
344                        pow_paren.append(item)
345                    b.append(item.base)
346            elif item.is_Rational and item is not S.Infinity:
347                if item.p != 1:
348                    a.append(Rational(item.p))
349                if item.q != 1:
350                    b.append(Rational(item.q))
351            else:
352                a.append(item)
353
354        a = a or [S.One]
355
356        a_str = [self.parenthesize(x, prec, strict=False) for x in a]
357        b_str = [self.parenthesize(x, prec, strict=False) for x in b]
358
359        # To parenthesize Pow with exp = -1 and having more than one Symbol
360        for item in pow_paren:
361            if item.base in b:
362                b_str[b.index(item.base)] = "(%s)" % b_str[b.index(item.base)]
363
364        if not b:
365            return sign + '*'.join(a_str)
366        elif len(b) == 1:
367            return sign + '*'.join(a_str) + "/" + b_str[0]
368        else:
369            return sign + '*'.join(a_str) + "/(%s)" % '*'.join(b_str)
370
371    def _print_MatMul(self, expr):
372        c, m = expr.as_coeff_mmul()
373
374        sign = ""
375        if c.is_number:
376            re, im = c.as_real_imag()
377            if im.is_zero and re.is_negative:
378                expr = _keep_coeff(-c, m)
379                sign = "-"
380            elif re.is_zero and im.is_negative:
381                expr = _keep_coeff(-c, m)
382                sign = "-"
383
384        return sign + '*'.join(
385            [self.parenthesize(arg, precedence(expr)) for arg in expr.args]
386        )
387
388    def _print_ElementwiseApplyFunction(self, expr):
389        return "{}.({})".format(
390            expr.function,
391            self._print(expr.expr),
392        )
393
394    def _print_NaN(self, expr):
395        return 'nan'
396
397    def _print_NegativeInfinity(self, expr):
398        return '-oo'
399
400    def _print_Order(self, expr):
401        if not expr.variables or all(p is S.Zero for p in expr.point):
402            if len(expr.variables) <= 1:
403                return 'O(%s)' % self._print(expr.expr)
404            else:
405                return 'O(%s)' % self.stringify((expr.expr,) + expr.variables, ', ', 0)
406        else:
407            return 'O(%s)' % self.stringify(expr.args, ', ', 0)
408
409    def _print_Ordinal(self, expr):
410        return expr.__str__()
411
412    def _print_Cycle(self, expr):
413        return expr.__str__()
414
415    def _print_Permutation(self, expr):
416        from sympy.combinatorics.permutations import Permutation, Cycle
417        from sympy.utilities.exceptions import SymPyDeprecationWarning
418
419        perm_cyclic = Permutation.print_cyclic
420        if perm_cyclic is not None:
421            SymPyDeprecationWarning(
422                feature="Permutation.print_cyclic = {}".format(perm_cyclic),
423                useinstead="init_printing(perm_cyclic={})"
424                .format(perm_cyclic),
425                issue=15201,
426                deprecated_since_version="1.6").warn()
427        else:
428            perm_cyclic = self._settings.get("perm_cyclic", True)
429
430        if perm_cyclic:
431            if not expr.size:
432                return '()'
433            # before taking Cycle notation, see if the last element is
434            # a singleton and move it to the head of the string
435            s = Cycle(expr)(expr.size - 1).__repr__()[len('Cycle'):]
436            last = s.rfind('(')
437            if not last == 0 and ',' not in s[last:]:
438                s = s[last:] + s[:last]
439            s = s.replace(',', '')
440            return s
441        else:
442            s = expr.support()
443            if not s:
444                if expr.size < 5:
445                    return 'Permutation(%s)' % self._print(expr.array_form)
446                return 'Permutation([], size=%s)' % self._print(expr.size)
447            trim = self._print(expr.array_form[:s[-1] + 1]) + ', size=%s' % self._print(expr.size)
448            use = full = self._print(expr.array_form)
449            if len(trim) < len(full):
450                use = trim
451            return 'Permutation(%s)' % use
452
453    def _print_Subs(self, obj):
454        expr, old, new = obj.args
455        if len(obj.point) == 1:
456            old = old[0]
457            new = new[0]
458        return "Subs(%s, %s, %s)" % (
459            self._print(expr), self._print(old), self._print(new))
460
461    def _print_TensorIndex(self, expr):
462        return expr._print()
463
464    def _print_TensorHead(self, expr):
465        return expr._print()
466
467    def _print_Tensor(self, expr):
468        return expr._print()
469
470    def _print_TensMul(self, expr):
471        # prints expressions like "A(a)", "3*A(a)", "(1+x)*A(a)"
472        sign, args = expr._get_args_for_traditional_printer()
473        return sign + "*".join(
474            [self.parenthesize(arg, precedence(expr)) for arg in args]
475        )
476
477    def _print_TensAdd(self, expr):
478        return expr._print()
479
480    def _print_ArraySymbol(self, expr):
481        return self._print(expr.name)
482
483    def _print_ArrayElement(self, expr):
484        return "%s[%s]" % (expr.name, ", ".join([self._print(i) for i in expr.indices]))
485
486    def _print_PermutationGroup(self, expr):
487        p = ['    %s' % self._print(a) for a in expr.args]
488        return 'PermutationGroup([\n%s])' % ',\n'.join(p)
489
490    def _print_Pi(self, expr):
491        return 'pi'
492
493    def _print_PolyRing(self, ring):
494        return "Polynomial ring in %s over %s with %s order" % \
495            (", ".join(map(lambda rs: self._print(rs), ring.symbols)),
496            self._print(ring.domain), self._print(ring.order))
497
498    def _print_FracField(self, field):
499        return "Rational function field in %s over %s with %s order" % \
500            (", ".join(map(lambda fs: self._print(fs), field.symbols)),
501            self._print(field.domain), self._print(field.order))
502
503    def _print_FreeGroupElement(self, elm):
504        return elm.__str__()
505
506    def _print_GaussianElement(self, poly):
507        return "(%s + %s*I)" % (poly.x, poly.y)
508
509    def _print_PolyElement(self, poly):
510        return poly.str(self, PRECEDENCE, "%s**%s", "*")
511
512    def _print_FracElement(self, frac):
513        if frac.denom == 1:
514            return self._print(frac.numer)
515        else:
516            numer = self.parenthesize(frac.numer, PRECEDENCE["Mul"], strict=True)
517            denom = self.parenthesize(frac.denom, PRECEDENCE["Atom"], strict=True)
518            return numer + "/" + denom
519
520    def _print_Poly(self, expr):
521        ATOM_PREC = PRECEDENCE["Atom"] - 1
522        terms, gens = [], [ self.parenthesize(s, ATOM_PREC) for s in expr.gens ]
523
524        for monom, coeff in expr.terms():
525            s_monom = []
526
527            for i, e in enumerate(monom):
528                if e > 0:
529                    if e == 1:
530                        s_monom.append(gens[i])
531                    else:
532                        s_monom.append(gens[i] + "**%d" % e)
533
534            s_monom = "*".join(s_monom)
535
536            if coeff.is_Add:
537                if s_monom:
538                    s_coeff = "(" + self._print(coeff) + ")"
539                else:
540                    s_coeff = self._print(coeff)
541            else:
542                if s_monom:
543                    if coeff is S.One:
544                        terms.extend(['+', s_monom])
545                        continue
546
547                    if coeff is S.NegativeOne:
548                        terms.extend(['-', s_monom])
549                        continue
550
551                s_coeff = self._print(coeff)
552
553            if not s_monom:
554                s_term = s_coeff
555            else:
556                s_term = s_coeff + "*" + s_monom
557
558            if s_term.startswith('-'):
559                terms.extend(['-', s_term[1:]])
560            else:
561                terms.extend(['+', s_term])
562
563        if terms[0] in ['-', '+']:
564            modifier = terms.pop(0)
565
566            if modifier == '-':
567                terms[0] = '-' + terms[0]
568
569        format = expr.__class__.__name__ + "(%s, %s"
570
571        from sympy.polys.polyerrors import PolynomialError
572
573        try:
574            format += ", modulus=%s" % expr.get_modulus()
575        except PolynomialError:
576            format += ", domain='%s'" % expr.get_domain()
577
578        format += ")"
579
580        for index, item in enumerate(gens):
581            if len(item) > 2 and (item[:1] == "(" and item[len(item) - 1:] == ")"):
582                gens[index] = item[1:len(item) - 1]
583
584        return format % (' '.join(terms), ', '.join(gens))
585
586    def _print_UniversalSet(self, p):
587        return 'UniversalSet'
588
589    def _print_AlgebraicNumber(self, expr):
590        if expr.is_aliased:
591            return self._print(expr.as_poly().as_expr())
592        else:
593            return self._print(expr.as_expr())
594
595    def _print_Pow(self, expr, rational=False):
596        """Printing helper function for ``Pow``
597
598        Parameters
599        ==========
600
601        rational : bool, optional
602            If ``True``, it will not attempt printing ``sqrt(x)`` or
603            ``x**S.Half`` as ``sqrt``, and will use ``x**(1/2)``
604            instead.
605
606            See examples for additional details
607
608        Examples
609        ========
610
611        >>> from sympy.functions import sqrt
612        >>> from sympy.printing.str import StrPrinter
613        >>> from sympy.abc import x
614
615        How ``rational`` keyword works with ``sqrt``:
616
617        >>> printer = StrPrinter()
618        >>> printer._print_Pow(sqrt(x), rational=True)
619        'x**(1/2)'
620        >>> printer._print_Pow(sqrt(x), rational=False)
621        'sqrt(x)'
622        >>> printer._print_Pow(1/sqrt(x), rational=True)
623        'x**(-1/2)'
624        >>> printer._print_Pow(1/sqrt(x), rational=False)
625        '1/sqrt(x)'
626
627        Notes
628        =====
629
630        ``sqrt(x)`` is canonicalized as ``Pow(x, S.Half)`` in SymPy,
631        so there is no need of defining a separate printer for ``sqrt``.
632        Instead, it should be handled here as well.
633        """
634        PREC = precedence(expr)
635
636        if expr.exp is S.Half and not rational:
637            return "sqrt(%s)" % self._print(expr.base)
638
639        if expr.is_commutative:
640            if -expr.exp is S.Half and not rational:
641                # Note: Don't test "expr.exp == -S.Half" here, because that will
642                # match -0.5, which we don't want.
643                return "%s/sqrt(%s)" % tuple(map(lambda arg: self._print(arg), (S.One, expr.base)))
644            if expr.exp is -S.One:
645                # Similarly to the S.Half case, don't test with "==" here.
646                return '%s/%s' % (self._print(S.One),
647                                  self.parenthesize(expr.base, PREC, strict=False))
648
649        e = self.parenthesize(expr.exp, PREC, strict=False)
650        if self.printmethod == '_sympyrepr' and expr.exp.is_Rational and expr.exp.q != 1:
651            # the parenthesized exp should be '(Rational(a, b))' so strip parens,
652            # but just check to be sure.
653            if e.startswith('(Rational'):
654                return '%s**%s' % (self.parenthesize(expr.base, PREC, strict=False), e[1:-1])
655        return '%s**%s' % (self.parenthesize(expr.base, PREC, strict=False), e)
656
657    def _print_UnevaluatedExpr(self, expr):
658        return self._print(expr.args[0])
659
660    def _print_MatPow(self, expr):
661        PREC = precedence(expr)
662        return '%s**%s' % (self.parenthesize(expr.base, PREC, strict=False),
663                         self.parenthesize(expr.exp, PREC, strict=False))
664
665    def _print_Integer(self, expr):
666        if self._settings.get("sympy_integers", False):
667            return "S(%s)" % (expr)
668        return str(expr.p)
669
670    def _print_Integers(self, expr):
671        return 'Integers'
672
673    def _print_Naturals(self, expr):
674        return 'Naturals'
675
676    def _print_Naturals0(self, expr):
677        return 'Naturals0'
678
679    def _print_Rationals(self, expr):
680        return 'Rationals'
681
682    def _print_Reals(self, expr):
683        return 'Reals'
684
685    def _print_Complexes(self, expr):
686        return 'Complexes'
687
688    def _print_EmptySet(self, expr):
689        return 'EmptySet'
690
691    def _print_EmptySequence(self, expr):
692        return 'EmptySequence'
693
694    def _print_int(self, expr):
695        return str(expr)
696
697    def _print_mpz(self, expr):
698        return str(expr)
699
700    def _print_Rational(self, expr):
701        if expr.q == 1:
702            return str(expr.p)
703        else:
704            if self._settings.get("sympy_integers", False):
705                return "S(%s)/%s" % (expr.p, expr.q)
706            return "%s/%s" % (expr.p, expr.q)
707
708    def _print_PythonRational(self, expr):
709        if expr.q == 1:
710            return str(expr.p)
711        else:
712            return "%d/%d" % (expr.p, expr.q)
713
714    def _print_Fraction(self, expr):
715        if expr.denominator == 1:
716            return str(expr.numerator)
717        else:
718            return "%s/%s" % (expr.numerator, expr.denominator)
719
720    def _print_mpq(self, expr):
721        if expr.denominator == 1:
722            return str(expr.numerator)
723        else:
724            return "%s/%s" % (expr.numerator, expr.denominator)
725
726    def _print_Float(self, expr):
727        prec = expr._prec
728        if prec < 5:
729            dps = 0
730        else:
731            dps = prec_to_dps(expr._prec)
732        if self._settings["full_prec"] is True:
733            strip = False
734        elif self._settings["full_prec"] is False:
735            strip = True
736        elif self._settings["full_prec"] == "auto":
737            strip = self._print_level > 1
738        low = self._settings["min"] if "min" in self._settings else None
739        high = self._settings["max"] if "max" in self._settings else None
740        rv = mlib_to_str(expr._mpf_, dps, strip_zeros=strip, min_fixed=low, max_fixed=high)
741        if rv.startswith('-.0'):
742            rv = '-0.' + rv[3:]
743        elif rv.startswith('.0'):
744            rv = '0.' + rv[2:]
745        if rv.startswith('+'):
746            # e.g., +inf -> inf
747            rv = rv[1:]
748        return rv
749
750    def _print_Relational(self, expr):
751
752        charmap = {
753            "==": "Eq",
754            "!=": "Ne",
755            ":=": "Assignment",
756            '+=': "AddAugmentedAssignment",
757            "-=": "SubAugmentedAssignment",
758            "*=": "MulAugmentedAssignment",
759            "/=": "DivAugmentedAssignment",
760            "%=": "ModAugmentedAssignment",
761        }
762
763        if expr.rel_op in charmap:
764            return '%s(%s, %s)' % (charmap[expr.rel_op], self._print(expr.lhs),
765                                   self._print(expr.rhs))
766
767        return '%s %s %s' % (self.parenthesize(expr.lhs, precedence(expr)),
768                           self._relationals.get(expr.rel_op) or expr.rel_op,
769                           self.parenthesize(expr.rhs, precedence(expr)))
770
771    def _print_ComplexRootOf(self, expr):
772        return "CRootOf(%s, %d)" % (self._print_Add(expr.expr,  order='lex'),
773                                    expr.index)
774
775    def _print_RootSum(self, expr):
776        args = [self._print_Add(expr.expr, order='lex')]
777
778        if expr.fun is not S.IdentityFunction:
779            args.append(self._print(expr.fun))
780
781        return "RootSum(%s)" % ", ".join(args)
782
783    def _print_GroebnerBasis(self, basis):
784        cls = basis.__class__.__name__
785
786        exprs = [self._print_Add(arg, order=basis.order) for arg in basis.exprs]
787        exprs = "[%s]" % ", ".join(exprs)
788
789        gens = [ self._print(gen) for gen in basis.gens ]
790        domain = "domain='%s'" % self._print(basis.domain)
791        order = "order='%s'" % self._print(basis.order)
792
793        args = [exprs] + gens + [domain, order]
794
795        return "%s(%s)" % (cls, ", ".join(args))
796
797    def _print_set(self, s):
798        items = sorted(s, key=default_sort_key)
799
800        args = ', '.join(self._print(item) for item in items)
801        if not args:
802            return "set()"
803        return '{%s}' % args
804
805    def _print_FiniteSet(self, s):
806        items = sorted(s, key=default_sort_key)
807
808        args = ', '.join(self._print(item) for item in items)
809        if any(item.has(FiniteSet) for item in items):
810            return 'FiniteSet({})'.format(args)
811        return '{{{}}}'.format(args)
812
813    def _print_Partition(self, s):
814        items = sorted(s, key=default_sort_key)
815
816        args = ', '.join(self._print(arg) for arg in items)
817        return 'Partition({})'.format(args)
818
819    def _print_frozenset(self, s):
820        if not s:
821            return "frozenset()"
822        return "frozenset(%s)" % self._print_set(s)
823
824    def _print_Sum(self, expr):
825        def _xab_tostr(xab):
826            if len(xab) == 1:
827                return self._print(xab[0])
828            else:
829                return self._print((xab[0],) + tuple(xab[1:]))
830        L = ', '.join([_xab_tostr(l) for l in expr.limits])
831        return 'Sum(%s, %s)' % (self._print(expr.function), L)
832
833    def _print_Symbol(self, expr):
834        return expr.name
835    _print_MatrixSymbol = _print_Symbol
836    _print_RandomSymbol = _print_Symbol
837
838    def _print_Identity(self, expr):
839        return "I"
840
841    def _print_ZeroMatrix(self, expr):
842        return "0"
843
844    def _print_OneMatrix(self, expr):
845        return "1"
846
847    def _print_Predicate(self, expr):
848        return "Q.%s" % expr.name
849
850    def _print_str(self, expr):
851        return str(expr)
852
853    def _print_tuple(self, expr):
854        if len(expr) == 1:
855            return "(%s,)" % self._print(expr[0])
856        else:
857            return "(%s)" % self.stringify(expr, ", ")
858
859    def _print_Tuple(self, expr):
860        return self._print_tuple(expr)
861
862    def _print_Transpose(self, T):
863        return "%s.T" % self.parenthesize(T.arg, PRECEDENCE["Pow"])
864
865    def _print_Uniform(self, expr):
866        return "Uniform(%s, %s)" % (self._print(expr.a), self._print(expr.b))
867
868    def _print_Quantity(self, expr):
869        if self._settings.get("abbrev", False):
870            return "%s" % expr.abbrev
871        return "%s" % expr.name
872
873    def _print_Quaternion(self, expr):
874        s = [self.parenthesize(i, PRECEDENCE["Mul"], strict=True) for i in expr.args]
875        a = [s[0]] + [i+"*"+j for i, j in zip(s[1:], "ijk")]
876        return " + ".join(a)
877
878    def _print_Dimension(self, expr):
879        return str(expr)
880
881    def _print_Wild(self, expr):
882        return expr.name + '_'
883
884    def _print_WildFunction(self, expr):
885        return expr.name + '_'
886
887    def _print_WildDot(self, expr):
888        return expr.name
889
890    def _print_WildPlus(self, expr):
891        return expr.name
892
893    def _print_WildStar(self, expr):
894        return expr.name
895
896    def _print_Zero(self, expr):
897        if self._settings.get("sympy_integers", False):
898            return "S(0)"
899        return "0"
900
901    def _print_DMP(self, p):
902        from sympy.core.sympify import SympifyError
903        try:
904            if p.ring is not None:
905                # TODO incorporate order
906                return self._print(p.ring.to_sympy(p))
907        except SympifyError:
908            pass
909
910        cls = p.__class__.__name__
911        rep = self._print(p.rep)
912        dom = self._print(p.dom)
913        ring = self._print(p.ring)
914
915        return "%s(%s, %s, %s)" % (cls, rep, dom, ring)
916
917    def _print_DMF(self, expr):
918        return self._print_DMP(expr)
919
920    def _print_Object(self, obj):
921        return 'Object("%s")' % obj.name
922
923    def _print_IdentityMorphism(self, morphism):
924        return 'IdentityMorphism(%s)' % morphism.domain
925
926    def _print_NamedMorphism(self, morphism):
927        return 'NamedMorphism(%s, %s, "%s")' % \
928               (morphism.domain, morphism.codomain, morphism.name)
929
930    def _print_Category(self, category):
931        return 'Category("%s")' % category.name
932
933    def _print_Manifold(self, manifold):
934        return manifold.name.name
935
936    def _print_Patch(self, patch):
937        return patch.name.name
938
939    def _print_CoordSystem(self, coords):
940        return coords.name.name
941
942    def _print_BaseScalarField(self, field):
943        return field._coord_sys.symbols[field._index].name
944
945    def _print_BaseVectorField(self, field):
946        return 'e_%s' % field._coord_sys.symbols[field._index].name
947
948    def _print_Differential(self, diff):
949        field = diff._form_field
950        if hasattr(field, '_coord_sys'):
951            return 'd%s' % field._coord_sys.symbols[field._index].name
952        else:
953            return 'd(%s)' % self._print(field)
954
955    def _print_Tr(self, expr):
956        #TODO : Handle indices
957        return "%s(%s)" % ("Tr", self._print(expr.args[0]))
958
959    def _print_Str(self, s):
960        return self._print(s.name)
961
962    def _print_AppliedBinaryRelation(self, expr):
963        rel = expr.function
964        return '%s(%s, %s)' % (self._print(rel),
965                               self._print(expr.lhs),
966                               self._print(expr.rhs))
967
968
969@print_function(StrPrinter)
970def sstr(expr, **settings):
971    """Returns the expression as a string.
972
973    For large expressions where speed is a concern, use the setting
974    order='none'. If abbrev=True setting is used then units are printed in
975    abbreviated form.
976
977    Examples
978    ========
979
980    >>> from sympy import symbols, Eq, sstr
981    >>> a, b = symbols('a b')
982    >>> sstr(Eq(a + b, 0))
983    'Eq(a + b, 0)'
984    """
985
986    p = StrPrinter(settings)
987    s = p.doprint(expr)
988
989    return s
990
991
992class StrReprPrinter(StrPrinter):
993    """(internal) -- see sstrrepr"""
994
995    def _print_str(self, s):
996        return repr(s)
997
998    def _print_Str(self, s):
999        # Str does not to be printed same as str here
1000        return "%s(%s)" % (s.__class__.__name__, self._print(s.name))
1001
1002
1003@print_function(StrReprPrinter)
1004def sstrrepr(expr, **settings):
1005    """return expr in mixed str/repr form
1006
1007       i.e. strings are returned in repr form with quotes, and everything else
1008       is returned in str form.
1009
1010       This function could be useful for hooking into sys.displayhook
1011    """
1012
1013    p = StrReprPrinter(settings)
1014    s = p.doprint(expr)
1015
1016    return s
1017