unicode.cpp
1 #include <limits>
2 #include <symengine/printers/strprinter.h>
3 #include <symengine/printers/unicode.h>
4 
5 namespace SymEngine
6 {
8 struct PrinterBasicCmp {
10  bool operator()(const RCP<const Basic> &x, const RCP<const Basic> &y) const
11  {
12  if (x->__eq__(*y))
13  return false;
14  return x->__cmp__(*y) == -1;
15  }
16 };
17 
18 void UnicodePrinter::bvisit(const Basic &x)
19 {
21  s << "<" << typeName<Basic>(x) << " instance at " << (const void *)this
22  << ">";
23  StringBox box(s.str());
24  box_ = box;
25 }
26 
27 void UnicodePrinter::bvisit(const Symbol &x)
28 {
29  box_ = StringBox(x.get_name());
30 }
31 
32 void UnicodePrinter::bvisit(const Infty &x)
33 {
34  if (x.is_negative_infinity())
35  box_ = StringBox("-\u221E", 2);
36  else if (x.is_positive_infinity())
37  box_ = StringBox("\u221E", 1);
38  else
39  box_ = StringBox("\U0001D467\u221E", 2);
40 }
41 
42 void UnicodePrinter::bvisit(const NaN &x)
43 {
44  box_ = StringBox("NaN");
45 }
46 
47 void UnicodePrinter::bvisit(const Integer &x)
48 {
50  s << x.as_integer_class();
51  box_ = StringBox(s.str());
52 }
53 
54 void UnicodePrinter::bvisit(const Rational &x)
55 {
57  num << (*x.get_num()).as_integer_class();
58  StringBox rat(num.str());
59  std::ostringstream denom;
60  denom << (*x.get_den()).as_integer_class();
61  StringBox denbox(denom.str());
62  rat.add_below_unicode_line(denbox);
63  box_ = rat;
64 }
65 
66 void UnicodePrinter::bvisit(const Complex &x)
67 {
69  bool mul = false;
70  if (x.real_ != 0) {
71  s << x.real_;
72  // Since Complex is in canonical form, imaginary_ is not 0.
73  if (mp_sign(x.imaginary_) == 1) {
74  s << " + ";
75  } else {
76  s << " - ";
77  }
78  // If imaginary_ is not 1 or -1, print the absolute value
79  if (x.imaginary_ != mp_sign(x.imaginary_)) {
80  s << mp_abs(x.imaginary_);
81  s << "\u22C5" << get_imag_symbol();
82  mul = true;
83  } else {
84  s << get_imag_symbol();
85  }
86  } else {
87  if (x.imaginary_ != mp_sign(x.imaginary_)) {
88  s << x.imaginary_;
89  s << "\u22C5" << get_imag_symbol();
90  mul = true;
91  } else {
92  if (mp_sign(x.imaginary_) == 1) {
93  s << get_imag_symbol();
94  } else {
95  s << "-" << get_imag_symbol();
96  }
97  }
98  }
99  std::string str = s.str();
100  std::size_t width = str.length() - 3;
101  if (mul)
102  width--;
103  StringBox box(str, width);
104  box_ = box;
105 }
106 
107 void UnicodePrinter::bvisit(const RealDouble &x)
108 {
109  box_ = StringBox(print_double(x.i));
110 }
111 
112 void UnicodePrinter::bvisit(const ComplexDouble &x)
113 {
114  std::string str = print_double(x.i.real());
115  if (x.i.imag() < 0) {
116  str += " - " + print_double(-x.i.imag());
117  } else {
118  str += " + " + print_double(x.i.imag());
119  }
120  auto len = str.length();
121  str += "\u22C5" + get_imag_symbol();
122  box_ = StringBox(str, len + 2);
123 }
124 
125 void UnicodePrinter::bvisit(const Equality &x)
126 {
127  StringBox box = apply(x.get_arg1());
128  StringBox eq(" = ");
129  box.add_right(eq);
130  StringBox rhs = apply(x.get_arg2());
131  box.add_right(rhs);
132  box_ = box;
133 }
134 
135 void UnicodePrinter::bvisit(const Unequality &x)
136 {
137  StringBox box = apply(x.get_arg1());
138  StringBox eq(" \u2260 ", 3);
139  box.add_right(eq);
140  StringBox rhs = apply(x.get_arg2());
141  box.add_right(rhs);
142  box_ = box;
143 }
144 
145 void UnicodePrinter::bvisit(const LessThan &x)
146 {
147  StringBox box = apply(x.get_arg1());
148  StringBox eq(" \u2264 ", 3);
149  box.add_right(eq);
150  StringBox rhs = apply(x.get_arg2());
151  box.add_right(rhs);
152  box_ = box;
153 }
154 
155 void UnicodePrinter::bvisit(const StrictLessThan &x)
156 {
157  StringBox box = apply(x.get_arg1());
158  StringBox eq(" < ", 3);
159  box.add_right(eq);
160  StringBox rhs = apply(x.get_arg2());
161  box.add_right(rhs);
162  box_ = box;
163 }
164 
165 void UnicodePrinter::bvisit(const Interval &x)
166 {
167  StringBox box = apply(x.get_start());
168  StringBox comma = StringBox(", ");
169  box.add_right(comma);
170  StringBox end = StringBox(apply(x.get_end()));
171  box.add_right(end);
172  if (x.get_left_open()) {
173  box.add_left_parens();
174  } else {
175  box.add_left_sqbracket();
176  }
177  if (x.get_right_open())
178  box.add_right_parens();
179  else
180  box.add_right_sqbracket();
181  box_ = box;
182 }
183 
184 void UnicodePrinter::bvisit(const BooleanAtom &x)
185 {
186  if (x.get_val()) {
187  box_ = StringBox("true");
188  } else {
189  box_ = StringBox("false");
190  }
191 }
192 
193 void UnicodePrinter::bvisit(const And &x)
194 {
195  auto container = x.get_container();
196  StringBox box = apply(*container.begin());
197  StringBox op(" \u2227 ", 3);
198  for (auto it = ++(container.begin()); it != container.end(); ++it) {
199  box.add_right(op);
200  StringBox next = apply(*it);
201  box.add_right(next);
202  }
203  box_ = box;
204 }
205 
206 void UnicodePrinter::bvisit(const Or &x)
207 {
208  auto container = x.get_container();
209  StringBox box = apply(*container.begin());
210  StringBox op(" \u2228 ", 3);
211  for (auto it = ++(container.begin()); it != container.end(); ++it) {
212  box.add_right(op);
213  StringBox next = apply(*it);
214  box.add_right(next);
215  }
216  box_ = box;
217 }
218 
219 void UnicodePrinter::bvisit(const Xor &x)
220 {
221  auto container = x.get_container();
222  StringBox box = apply(*container.begin());
223  StringBox op(" \u22BB ", 3);
224  for (auto it = ++(container.begin()); it != container.end(); ++it) {
225  box.add_right(op);
226  StringBox next = apply(*it);
227  box.add_right(next);
228  }
229  box_ = box;
230 }
231 
232 void UnicodePrinter::bvisit(const Not &x)
233 {
234  StringBox box("\u00AC", 1);
235  StringBox expr = apply(*x.get_arg());
236  expr.enclose_parens();
237  box.add_right(expr);
238  box_ = box;
239 }
240 
241 void UnicodePrinter::bvisit(const Contains &x)
242 {
243  StringBox s = apply(x.get_expr());
244  StringBox op(" \u220A ", 3);
245  s.add_right(op);
246  auto right = apply(x.get_set());
247  s.add_right(right);
248  box_ = s;
249 }
250 
251 void UnicodePrinter::bvisit(const Piecewise &x)
252 {
253  StringBox box;
254 
255  auto vec = x.get_vec();
256  auto it = vec.begin();
257  while (true) {
258  StringBox piece = apply((*it).first);
259  StringBox mid(" if ");
260  piece.add_right(mid);
261  StringBox second = apply((*it).second);
262  piece.add_right(second);
263  box.add_below(piece);
264  ++it;
265  if (it == vec.end()) {
266  break;
267  }
268  }
269  box.add_left_curly();
270  box_ = box;
271 }
272 
273 void UnicodePrinter::bvisit(const Complexes &x)
274 {
275  box_ = StringBox("\u2102", 1);
276 }
277 
278 void UnicodePrinter::bvisit(const Reals &x)
279 {
280  box_ = StringBox("\u211D", 1);
281 }
282 
283 void UnicodePrinter::bvisit(const Rationals &x)
284 {
285  box_ = StringBox("\u211A", 1);
286 }
287 
288 void UnicodePrinter::bvisit(const Integers &x)
289 {
290  box_ = StringBox("\u2124", 1);
291 }
292 
293 void UnicodePrinter::bvisit(const EmptySet &x)
294 {
295  box_ = StringBox("\u2205", 1);
296 }
297 
298 void UnicodePrinter::bvisit(const UniversalSet &x)
299 {
300  box_ = StringBox("\U0001D54C", 1);
301 }
302 
303 void UnicodePrinter::bvisit(const Union &x)
304 {
305  auto container = x.get_container();
306  StringBox box = apply(*container.begin());
307  StringBox op(" \u222A ", 3);
308  for (auto it = ++(container.begin()); it != container.end(); ++it) {
309  box.add_right(op);
310  StringBox next = apply(*it);
311  box.add_right(next);
312  }
313  box_ = box;
314 }
315 
316 void UnicodePrinter::bvisit(const Complement &x)
317 {
318  StringBox box = apply(*x.get_universe());
319  StringBox op(" \\ ");
320  box.add_right(op);
321  StringBox rhs = apply(*x.get_container());
322  box.add_right(rhs);
323  box_ = box;
324 }
325 
326 void UnicodePrinter::bvisit(const ImageSet &x)
327 {
328  StringBox box = apply(*x.get_expr());
329  StringBox bar(" | ");
330  box.add_right(bar);
331  StringBox symbol = apply(*x.get_symbol());
332  box.add_right(symbol);
333  StringBox in(" \u220A ", 3);
334  box.add_right(in);
335  StringBox base = apply(*x.get_baseset());
336  box.add_right(base);
337  box.enclose_curlies();
338  box_ = box;
339 }
340 
341 void UnicodePrinter::bvisit(const FiniteSet &x)
342 {
343  StringBox box;
344  StringBox comma(", ");
345  bool first = true;
346  for (const auto &elem : x.get_container()) {
347  if (not first) {
348  box.add_right(comma);
349  } else {
350  first = false;
351  }
352  StringBox arg = apply(elem);
353  box.add_right(arg);
354  }
355  box.enclose_curlies();
356  box_ = box;
357 }
358 
359 void UnicodePrinter::bvisit(const ConditionSet &x)
360 {
361  StringBox box = apply(*x.get_symbol());
362  StringBox bar(" | ");
363  box.add_right(bar);
364  StringBox cond = apply(*x.get_condition());
365  box.add_right(cond);
366  box.enclose_curlies();
367  box_ = box;
368 }
369 
370 void UnicodePrinter::bvisit(const Add &x)
371 {
372  StringBox box;
373  bool first = true;
374  std::map<RCP<const Basic>, RCP<const Number>, PrinterBasicCmp> dict(
375  x.get_dict().begin(), x.get_dict().end());
376 
377  if (neq(*(x.get_coef()), *zero)) {
378  box = apply(x.get_coef());
379  first = false;
380  }
381  bool minus = false;
382  for (const auto &p : dict) {
383  StringBox t;
384  if (eq(*(p.second), *one)) {
385  t = parenthesizeLT(p.first, PrecedenceEnum::Add);
386  } else if (eq(*(p.second), *minus_one)) {
387  minus = true;
388  t = parenthesizeLT(p.first, PrecedenceEnum::Mul);
389  } else {
390  if (down_cast<const Number &>(*p.second).is_negative()) {
391  minus = true;
392  }
393  // FIXME: Double minus here
394  t = parenthesizeLT(p.second, PrecedenceEnum::Mul);
395  auto op = print_mul();
396  t.add_right(op);
397  auto rhs = parenthesizeLT(p.first, PrecedenceEnum::Mul);
398  t.add_right(rhs);
399  }
400 
401  if (not first) {
402  if (minus) {
403  StringBox op(" - ");
404  box.add_right(op);
405  box.add_right(t);
406  minus = false;
407  } else {
408  StringBox op(" + ");
409  box.add_right(op);
410  box.add_right(t);
411  }
412  } else {
413  box.add_right(t);
414  first = false;
415  }
416  }
417  box_ = box;
418 }
419 
420 void UnicodePrinter::_print_pow(const RCP<const Basic> &a,
421  const RCP<const Basic> &b)
422 {
423  if (eq(*b, *rational(1, 2))) {
424  StringBox box = apply(a);
425  box.enclose_sqrt();
426  box_ = box;
427  } else {
428  StringBox base = parenthesizeLE(a, PrecedenceEnum::Pow);
429  StringBox exp = parenthesizeLE(b, PrecedenceEnum::Pow);
430  base.add_power(exp);
431  box_ = base;
432  }
433 }
434 
435 void UnicodePrinter::bvisit(const Mul &x)
436 {
437  StringBox box1, box2;
438  bool num = false;
439  unsigned den = 0;
440  StringBox mulbox = print_mul();
441 
442  bool first_box1 = true;
443  bool first_box2 = true;
444 
445  if (eq(*(x.get_coef()), *minus_one)) {
446  box1 = StringBox("-");
447  } else if (neq(*(x.get_coef()), *one)) {
448  RCP<const Basic> numer, denom;
449  as_numer_denom(x.get_coef(), outArg(numer), outArg(denom));
450  if (neq(*numer, *one)) {
451  num = true;
452  box1 = parenthesizeLT(numer, PrecedenceEnum::Mul);
453  first_box1 = false;
454  }
455  if (neq(*denom, *one)) {
456  den++;
457  box2 = parenthesizeLT(denom, PrecedenceEnum::Mul);
458  first_box2 = false;
459  }
460  }
461 
462  for (const auto &p : x.get_dict()) {
463  if ((is_a<Integer>(*p.second) or is_a<Rational>(*p.second))
464  and down_cast<const Number &>(*p.second).is_negative()) {
465  if (not first_box2) {
466  box2.add_right(mulbox);
467  } else {
468  first_box2 = false;
469  }
470  if (eq(*(p.second), *minus_one)) {
471  auto expr = parenthesizeLT(p.first, PrecedenceEnum::Mul);
472  box2.add_right(expr);
473  } else {
474  _print_pow(p.first, neg(p.second));
475  box2.add_right(box_);
476  }
477  den++;
478  } else {
479  if (not first_box1) {
480  box1.add_right(mulbox);
481  } else {
482  first_box1 = false;
483  }
484  if (eq(*(p.second), *one)) {
485  auto expr = parenthesizeLT(p.first, PrecedenceEnum::Mul);
486  box1.add_right(expr);
487  } else {
488  _print_pow(p.first, p.second);
489  box1.add_right(box_);
490  }
491  num = true;
492  }
493  }
494 
495  if (not num) {
496  auto onebox = StringBox("1");
497  box1.add_right(onebox);
498  box1.add_right(mulbox);
499  }
500 
501  if (den != 0) {
502  if (den > 1) {
503  box2.enclose_parens();
504  }
505  box1.add_below_unicode_line(box2);
506  }
507  box_ = box1;
508 }
509 
510 void UnicodePrinter::bvisit(const Pow &x)
511 {
512  _print_pow(x.get_base(), x.get_exp());
513 }
514 
515 void UnicodePrinter::bvisit(const Constant &x)
516 {
517  // NOTE: Using italics for constants which is very common in mathematics
518  // typesetting. (It goes against the ISO typesetting-standard though.)
519  if (eq(x, *pi)) {
520  box_ = StringBox("\U0001D70B", 1);
521  } else if (eq(x, *E)) {
522  box_ = StringBox("\U0001D452", 1);
523  } else if (eq(x, *EulerGamma)) {
524  box_ = StringBox("\U0001D6FE", 1);
525  } else if (eq(x, *Catalan)) {
526  box_ = StringBox("\U0001D43A", 1);
527  } else if (eq(x, *GoldenRatio)) {
528  box_ = StringBox("\U0001D719", 1);
529  }
530 }
531 
532 StringBox UnicodePrinter::apply(const vec_basic &d)
533 {
534  StringBox box;
535  StringBox comma(", ");
536  for (auto p = d.begin(); p != d.end(); p++) {
537  if (p != d.begin()) {
538  box.add_right(comma);
539  }
540  StringBox arg = apply(*p);
541  box.add_right(arg);
542  }
543  return box;
544 }
545 
546 void UnicodePrinter::bvisit(const Abs &x)
547 {
548  StringBox box = apply(*x.get_arg());
549  box.enclose_abs();
550  box_ = box;
551 }
552 
553 void UnicodePrinter::bvisit(const Floor &x)
554 {
555  StringBox box = apply(*x.get_arg());
556  box.enclose_floor();
557  box_ = box;
558 }
559 
560 void UnicodePrinter::bvisit(const Ceiling &x)
561 {
562  StringBox box = apply(*x.get_arg());
563  box.enclose_ceiling();
564  box_ = box;
565 }
566 
567 void UnicodePrinter::bvisit(const Function &x)
568 {
569  StringBox box(names_[x.get_type_code()], lengths_[x.get_type_code()]);
570  vec_basic vec = x.get_args();
571  StringBox args = apply(vec);
572  args.enclose_parens();
573  box.add_right(args);
574  box_ = box;
575 }
576 
577 void UnicodePrinter::bvisit(const FunctionSymbol &x)
578 {
579  StringBox box(x.get_name());
580  StringBox args;
581  StringBox comma(", ");
582  bool first = true;
583  for (auto arg : x.get_args()) {
584  if (first) {
585  first = false;
586  } else {
587  args.add_right(comma);
588  }
589  StringBox argbox = apply(arg);
590  args.add_right(argbox);
591  }
592  args.enclose_parens();
593  box.add_right(args);
594  box_ = box;
595 }
596 
597 StringBox UnicodePrinter::parenthesizeLT(const RCP<const Basic> &x,
598  PrecedenceEnum precedenceEnum)
599 {
600  Precedence prec;
601  if (prec.getPrecedence(x) < precedenceEnum) {
602  auto box = apply(x);
603  box.enclose_parens();
604  return box;
605  } else {
606  return apply(x);
607  }
608 }
609 
610 StringBox UnicodePrinter::parenthesizeLE(const RCP<const Basic> &x,
611  PrecedenceEnum precedenceEnum)
612 {
613  Precedence prec;
614  if (prec.getPrecedence(x) <= precedenceEnum) {
615  auto box = apply(x);
616  box.enclose_parens();
617  return box;
618  } else {
619  return apply(x);
620  }
621 }
622 
623 StringBox UnicodePrinter::apply(const RCP<const Basic> &b)
624 {
625  b->accept(*this);
626  return box_;
627 }
628 
629 StringBox UnicodePrinter::apply(const Basic &b)
630 {
631  b.accept(*this);
632  return box_;
633 }
634 
635 static std::vector<std::string> init_unicode_printer_names()
636 {
637  std::vector<std::string> names = init_str_printer_names();
638  names[SYMENGINE_LAMBERTW] = "W";
639  names[SYMENGINE_ZETA] = "\U0001D701";
640  names[SYMENGINE_DIRICHLET_ETA] = "\U0001D702";
641  names[SYMENGINE_LOWERGAMMA] = "\U0001D6FE";
642  names[SYMENGINE_UPPERGAMMA] = "\u0393";
643  names[SYMENGINE_BETA] = "B";
644  names[SYMENGINE_LOGGAMMA] = "log \u0393";
645  names[SYMENGINE_GAMMA] = "\u0393";
646  names[SYMENGINE_PRIMEPI] = "\U0001D70B";
647  return names;
648 }
649 
650 const std::vector<std::string> UnicodePrinter::names_
651  = init_unicode_printer_names();
652 
654 init_unicode_printer_lengths(const std::vector<std::string> &names)
655 {
656  std::vector<size_t> lengths;
657  for (auto &name : names) {
658  lengths.push_back(name.length());
659  }
660  lengths[SYMENGINE_LAMBERTW] = 1;
661  lengths[SYMENGINE_ZETA] = 1;
662  lengths[SYMENGINE_DIRICHLET_ETA] = 1;
663  lengths[SYMENGINE_LOWERGAMMA] = 1;
664  lengths[SYMENGINE_UPPERGAMMA] = 1;
665  lengths[SYMENGINE_BETA] = 1;
666  lengths[SYMENGINE_LOGGAMMA] = 5;
667  lengths[SYMENGINE_GAMMA] = 1;
668  lengths[SYMENGINE_PRIMEPI] = 1;
669  return lengths;
670 }
671 
672 const std::vector<size_t> UnicodePrinter::lengths_
673  = init_unicode_printer_lengths(UnicodePrinter::names_);
674 
675 StringBox UnicodePrinter::print_mul()
676 {
677  return StringBox("\u22C5", 1);
678 }
679 
680 std::string UnicodePrinter::get_imag_symbol()
681 {
682  return "\U0001D456";
683 }
684 
685 std::string unicode(const Basic &x)
686 {
687  UnicodePrinter printer;
688  return printer.apply(x).get_string();
689 }
690 
691 } // namespace SymEngine
T end(T... args)
T right(T... args)
Main namespace for SymEngine package.
Definition: add.cpp:19
RCP< const Symbol > symbol(const std::string &name)
inline version to return Symbol
Definition: symbol.h:82
bool eq(const Basic &a, const Basic &b)
Checks equality for a and b
Definition: basic-inl.h:21
RCP< const Basic > exp(const RCP< const Basic > &x)
Returns the natural exponential function E**x = pow(E, x)
Definition: pow.cpp:270
RCP< const Basic > mul(const RCP< const Basic > &a, const RCP< const Basic > &b)
Multiplication.
Definition: mul.cpp:358
bool neq(const Basic &a, const Basic &b)
Checks inequality for a and b
Definition: basic-inl.h:29
RCP< const Number > rational(long n, long d)
convenience creator from two longs
Definition: rational.h:328
RCP< const Basic > neg(const RCP< const Basic > &a)
Negation.
Definition: mul.cpp:449
T next(T... args)
T push_back(T... args)
T str(T... args)
bool operator()(const RCP< const Basic > &x, const RCP< const Basic > &y) const
true if x < y, false otherwise
Definition: unicode.cpp:10