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 {
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 {
24  std::ostringstream s;
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 {
53  std::ostringstream s;
54  s << x.as_integer_class();
55  box_ = StringBox(s.str());
56 }
57 
58 void UnicodePrinter::bvisit(const Rational &x)
59 {
60  std::ostringstream num;
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 {
72  std::ostringstream s;
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>, PrinterBasicCmpUnicode> 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  if (minus) {
441  StringBox op("- ");
442  box.add_right(op);
443  box.add_right(t);
444  minus = false;
445  } else {
446  box.add_right(t);
447  }
448  first = false;
449  }
450  }
451  box_ = box;
452 }
453 
454 void UnicodePrinter::_print_pow(const RCP<const Basic> &a,
455  const RCP<const Basic> &b)
456 {
457  if (eq(*b, *rational(1, 2))) {
458  StringBox box = apply(a);
459  box.enclose_sqrt();
460  box_ = box;
461  } else {
462  StringBox base = parenthesizeLE(a, PrecedenceEnum::Pow);
463  StringBox exp = parenthesizeLE(b, PrecedenceEnum::Pow);
464  base.add_power(exp);
465  box_ = base;
466  }
467 }
468 
469 void UnicodePrinter::bvisit(const Mul &x)
470 {
471  StringBox box1, box2;
472  bool num = false;
473  unsigned den = 0;
474  StringBox mulbox = print_mul();
475 
476  bool first_box1 = true;
477  bool first_box2 = true;
478 
479  if (eq(*(x.get_coef()), *minus_one)) {
480  box1 = StringBox("-");
481  } else if (neq(*(x.get_coef()), *one)) {
482  RCP<const Basic> numer, denom;
483  as_numer_denom(x.get_coef(), outArg(numer), outArg(denom));
484  if (neq(*numer, *one)) {
485  num = true;
486  box1 = parenthesizeLT(numer, PrecedenceEnum::Mul);
487  first_box1 = false;
488  }
489  if (neq(*denom, *one)) {
490  den++;
491  box2 = parenthesizeLT(denom, PrecedenceEnum::Mul);
492  first_box2 = false;
493  }
494  }
495 
496  for (const auto &p : x.get_dict()) {
497  if ((is_a<Integer>(*p.second) or is_a<Rational>(*p.second))
498  and down_cast<const Number &>(*p.second).is_negative()) {
499  if (not first_box2) {
500  box2.add_right(mulbox);
501  } else {
502  first_box2 = false;
503  }
504  if (eq(*(p.second), *minus_one)) {
505  auto expr = parenthesizeLT(p.first, PrecedenceEnum::Mul);
506  box2.add_right(expr);
507  } else {
508  _print_pow(p.first, neg(p.second));
509  box2.add_right(box_);
510  }
511  den++;
512  } else {
513  if (not first_box1) {
514  box1.add_right(mulbox);
515  } else {
516  first_box1 = false;
517  }
518  if (eq(*(p.second), *one)) {
519  auto expr = parenthesizeLT(p.first, PrecedenceEnum::Mul);
520  box1.add_right(expr);
521  } else {
522  _print_pow(p.first, p.second);
523  box1.add_right(box_);
524  }
525  num = true;
526  }
527  }
528 
529  if (not num) {
530  auto onebox = StringBox("1");
531  box1.add_right(onebox);
532  box1.add_right(mulbox);
533  }
534 
535  if (den != 0) {
536  if (den > 1) {
537  box2.enclose_parens();
538  }
539  box1.add_below_unicode_line(box2);
540  }
541  box_ = box1;
542 }
543 
544 void UnicodePrinter::bvisit(const Pow &x)
545 {
546  _print_pow(x.get_base(), x.get_exp());
547 }
548 
549 void UnicodePrinter::bvisit(const Constant &x)
550 {
551  // NOTE: Using italics for constants which is very common in mathematics
552  // typesetting. (It goes against the ISO typesetting-standard though.)
553  if (eq(x, *pi)) {
554  box_ = StringBox(U8("\U0001D70B"), 1);
555  } else if (eq(x, *E)) {
556  box_ = StringBox(U8("\U0001D452"), 1);
557  } else if (eq(x, *EulerGamma)) {
558  box_ = StringBox(U8("\U0001D6FE"), 1);
559  } else if (eq(x, *Catalan)) {
560  box_ = StringBox(U8("\U0001D43A"), 1);
561  } else if (eq(x, *GoldenRatio)) {
562  box_ = StringBox(U8("\U0001D719"), 1);
563  }
564 }
565 
566 StringBox UnicodePrinter::apply(const vec_basic &d)
567 {
568  StringBox box("");
569  StringBox comma(", ");
570  for (auto p = d.begin(); p != d.end(); p++) {
571  if (p != d.begin()) {
572  box.add_right(comma);
573  }
574  StringBox arg = apply(*p);
575  box.add_right(arg);
576  }
577  return box;
578 }
579 
580 void UnicodePrinter::bvisit(const Abs &x)
581 {
582  StringBox box = apply(*x.get_arg());
583  box.enclose_abs();
584  box_ = box;
585 }
586 
587 void UnicodePrinter::bvisit(const Floor &x)
588 {
589  StringBox box = apply(*x.get_arg());
590  box.enclose_floor();
591  box_ = box;
592 }
593 
594 void UnicodePrinter::bvisit(const Ceiling &x)
595 {
596  StringBox box = apply(*x.get_arg());
597  box.enclose_ceiling();
598  box_ = box;
599 }
600 
601 static std::vector<std::string> init_unicode_printer_names()
602 {
603  std::vector<std::string> names = init_str_printer_names();
604  names[SYMENGINE_LAMBERTW] = "W";
605  names[SYMENGINE_ZETA] = U8("\U0001D701");
606  names[SYMENGINE_DIRICHLET_ETA] = U8("\U0001D702");
607  names[SYMENGINE_LOWERGAMMA] = U8("\U0001D6FE");
608  names[SYMENGINE_UPPERGAMMA] = U8("\u0393");
609  names[SYMENGINE_BETA] = U8("B");
610  names[SYMENGINE_LOGGAMMA] = U8("log \u0393");
611  names[SYMENGINE_GAMMA] = U8("\u0393");
612  names[SYMENGINE_PRIMEPI] = U8("\U0001D70B");
613  return names;
614 }
615 
616 static std::vector<size_t>
617 init_unicode_printer_lengths(const std::vector<std::string> &names)
618 {
619  std::vector<size_t> lengths;
620  for (auto &name : names) {
621  lengths.push_back(name.length());
622  }
623  lengths[SYMENGINE_LAMBERTW] = 1;
624  lengths[SYMENGINE_ZETA] = 1;
625  lengths[SYMENGINE_DIRICHLET_ETA] = 1;
626  lengths[SYMENGINE_LOWERGAMMA] = 1;
627  lengths[SYMENGINE_UPPERGAMMA] = 1;
628  lengths[SYMENGINE_BETA] = 1;
629  lengths[SYMENGINE_LOGGAMMA] = 5;
630  lengths[SYMENGINE_GAMMA] = 1;
631  lengths[SYMENGINE_PRIMEPI] = 1;
632  return lengths;
633 }
634 
635 void UnicodePrinter::bvisit(const Function &x)
636 {
637  static const std::vector<std::string> names_ = init_unicode_printer_names();
638  static const std::vector<size_t> lengths_
639  = init_unicode_printer_lengths(names_);
640  StringBox box(names_[x.get_type_code()], lengths_[x.get_type_code()]);
641  vec_basic vec = x.get_args();
642  StringBox args = apply(vec);
643  args.enclose_parens();
644  box.add_right(args);
645  box_ = box;
646 }
647 
648 void UnicodePrinter::bvisit(const FunctionSymbol &x)
649 {
650  StringBox box(x.get_name());
651  StringBox args;
652  StringBox comma(", ");
653  bool first = true;
654  for (auto arg : x.get_args()) {
655  if (first) {
656  first = false;
657  } else {
658  args.add_right(comma);
659  }
660  StringBox argbox = apply(arg);
661  args.add_right(argbox);
662  }
663  args.enclose_parens();
664  box.add_right(args);
665  box_ = box;
666 }
667 
668 void UnicodePrinter::bvisit(const Tuple &x)
669 {
670  vec_basic vec = x.get_args();
671  StringBox args = apply(vec);
672  args.enclose_parens();
673  box_ = args;
674 }
675 
676 StringBox UnicodePrinter::parenthesizeLT(const RCP<const Basic> &x,
677  PrecedenceEnum precedenceEnum)
678 {
679  Precedence prec;
680  if (prec.getPrecedence(x) < precedenceEnum) {
681  auto box = apply(x);
682  box.enclose_parens();
683  return box;
684  } else {
685  return apply(x);
686  }
687 }
688 
689 StringBox UnicodePrinter::parenthesizeLE(const RCP<const Basic> &x,
690  PrecedenceEnum precedenceEnum)
691 {
692  Precedence prec;
693  if (prec.getPrecedence(x) <= precedenceEnum) {
694  auto box = apply(x);
695  box.enclose_parens();
696  return box;
697  } else {
698  return apply(x);
699  }
700 }
701 
702 StringBox UnicodePrinter::apply(const RCP<const Basic> &b)
703 {
704  b->accept(*this);
705  return box_;
706 }
707 
708 StringBox UnicodePrinter::apply(const Basic &b)
709 {
710  b.accept(*this);
711  return box_;
712 }
713 
714 StringBox UnicodePrinter::print_mul()
715 {
716  return StringBox(U8("\u22C5"), 1);
717 }
718 
719 std::string UnicodePrinter::get_imag_symbol()
720 {
721  return U8("\U0001D456");
722 }
723 
724 std::string unicode(const Basic &x)
725 {
726  UnicodePrinter printer;
727  return printer.apply(x).get_string();
728 }
729 
730 } // namespace SymEngine
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:87
RCP< const Basic > mul(const RCP< const Basic > &a, const RCP< const Basic > &b)
Multiplication.
Definition: mul.cpp:352
RCP< const Basic > neg(const RCP< const Basic > &a)
Negation.
Definition: mul.cpp:443
RCP< const Basic > exp(const RCP< const Basic > &x)
Returns the natural exponential function E**x = pow(E, x)
Definition: pow.cpp:271
bool eq(const Basic &a, const Basic &b)
Checks equality for a and b
Definition: basic-inl.h:21
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:329
Less operator (<) using cmp:
Definition: unicode.cpp:12
bool operator()(const RCP< const Basic > &x, const RCP< const Basic > &y) const
true if x < y, false otherwise
Definition: unicode.cpp:14