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