pretty_symbology.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  1. """Symbolic primitives + unicode/ASCII abstraction for pretty.py"""
  2. import sys
  3. import warnings
  4. from string import ascii_lowercase, ascii_uppercase
  5. import unicodedata
  6. unicode_warnings = ''
  7. def U(name):
  8. """
  9. Get a unicode character by name or, None if not found.
  10. This exists because older versions of Python use older unicode databases.
  11. """
  12. try:
  13. return unicodedata.lookup(name)
  14. except KeyError:
  15. global unicode_warnings
  16. unicode_warnings += 'No \'%s\' in unicodedata\n' % name
  17. return None
  18. from sympy.printing.conventions import split_super_sub
  19. from sympy.core.alphabets import greeks
  20. from sympy.utilities.exceptions import sympy_deprecation_warning
  21. # prefix conventions when constructing tables
  22. # L - LATIN i
  23. # G - GREEK beta
  24. # D - DIGIT 0
  25. # S - SYMBOL +
  26. __all__ = ['greek_unicode', 'sub', 'sup', 'xsym', 'vobj', 'hobj', 'pretty_symbol',
  27. 'annotated', 'center_pad', 'center']
  28. _use_unicode = False
  29. def pretty_use_unicode(flag=None):
  30. """Set whether pretty-printer should use unicode by default"""
  31. global _use_unicode, unicode_warnings
  32. if flag is None:
  33. return _use_unicode
  34. if flag and unicode_warnings:
  35. # print warnings (if any) on first unicode usage
  36. warnings.warn(unicode_warnings)
  37. unicode_warnings = ''
  38. use_unicode_prev = _use_unicode
  39. _use_unicode = flag
  40. return use_unicode_prev
  41. def pretty_try_use_unicode():
  42. """See if unicode output is available and leverage it if possible"""
  43. encoding = getattr(sys.stdout, 'encoding', None)
  44. # this happens when e.g. stdout is redirected through a pipe, or is
  45. # e.g. a cStringIO.StringO
  46. if encoding is None:
  47. return # sys.stdout has no encoding
  48. symbols = []
  49. # see if we can represent greek alphabet
  50. symbols += greek_unicode.values()
  51. # and atoms
  52. symbols += atoms_table.values()
  53. for s in symbols:
  54. if s is None:
  55. return # common symbols not present!
  56. try:
  57. s.encode(encoding)
  58. except UnicodeEncodeError:
  59. return
  60. # all the characters were present and encodable
  61. pretty_use_unicode(True)
  62. def xstr(*args):
  63. sympy_deprecation_warning(
  64. """
  65. The sympy.printing.pretty.pretty_symbology.xstr() function is
  66. deprecated. Use str() instead.
  67. """,
  68. deprecated_since_version="1.7",
  69. active_deprecations_target="deprecated-pretty-printing-functions"
  70. )
  71. return str(*args)
  72. # GREEK
  73. g = lambda l: U('GREEK SMALL LETTER %s' % l.upper())
  74. G = lambda l: U('GREEK CAPITAL LETTER %s' % l.upper())
  75. greek_letters = list(greeks) # make a copy
  76. # deal with Unicode's funny spelling of lambda
  77. greek_letters[greek_letters.index('lambda')] = 'lamda'
  78. # {} greek letter -> (g,G)
  79. greek_unicode = {L: g(L) for L in greek_letters}
  80. greek_unicode.update((L[0].upper() + L[1:], G(L)) for L in greek_letters)
  81. # aliases
  82. greek_unicode['lambda'] = greek_unicode['lamda']
  83. greek_unicode['Lambda'] = greek_unicode['Lamda']
  84. greek_unicode['varsigma'] = '\N{GREEK SMALL LETTER FINAL SIGMA}'
  85. # BOLD
  86. b = lambda l: U('MATHEMATICAL BOLD SMALL %s' % l.upper())
  87. B = lambda l: U('MATHEMATICAL BOLD CAPITAL %s' % l.upper())
  88. bold_unicode = {l: b(l) for l in ascii_lowercase}
  89. bold_unicode.update((L, B(L)) for L in ascii_uppercase)
  90. # GREEK BOLD
  91. gb = lambda l: U('MATHEMATICAL BOLD SMALL %s' % l.upper())
  92. GB = lambda l: U('MATHEMATICAL BOLD CAPITAL %s' % l.upper())
  93. greek_bold_letters = list(greeks) # make a copy, not strictly required here
  94. # deal with Unicode's funny spelling of lambda
  95. greek_bold_letters[greek_bold_letters.index('lambda')] = 'lamda'
  96. # {} greek letter -> (g,G)
  97. greek_bold_unicode = {L: g(L) for L in greek_bold_letters}
  98. greek_bold_unicode.update((L[0].upper() + L[1:], G(L)) for L in greek_bold_letters)
  99. greek_bold_unicode['lambda'] = greek_unicode['lamda']
  100. greek_bold_unicode['Lambda'] = greek_unicode['Lamda']
  101. greek_bold_unicode['varsigma'] = '\N{MATHEMATICAL BOLD SMALL FINAL SIGMA}'
  102. digit_2txt = {
  103. '0': 'ZERO',
  104. '1': 'ONE',
  105. '2': 'TWO',
  106. '3': 'THREE',
  107. '4': 'FOUR',
  108. '5': 'FIVE',
  109. '6': 'SIX',
  110. '7': 'SEVEN',
  111. '8': 'EIGHT',
  112. '9': 'NINE',
  113. }
  114. symb_2txt = {
  115. '+': 'PLUS SIGN',
  116. '-': 'MINUS',
  117. '=': 'EQUALS SIGN',
  118. '(': 'LEFT PARENTHESIS',
  119. ')': 'RIGHT PARENTHESIS',
  120. '[': 'LEFT SQUARE BRACKET',
  121. ']': 'RIGHT SQUARE BRACKET',
  122. '{': 'LEFT CURLY BRACKET',
  123. '}': 'RIGHT CURLY BRACKET',
  124. # non-std
  125. '{}': 'CURLY BRACKET',
  126. 'sum': 'SUMMATION',
  127. 'int': 'INTEGRAL',
  128. }
  129. # SUBSCRIPT & SUPERSCRIPT
  130. LSUB = lambda letter: U('LATIN SUBSCRIPT SMALL LETTER %s' % letter.upper())
  131. GSUB = lambda letter: U('GREEK SUBSCRIPT SMALL LETTER %s' % letter.upper())
  132. DSUB = lambda digit: U('SUBSCRIPT %s' % digit_2txt[digit])
  133. SSUB = lambda symb: U('SUBSCRIPT %s' % symb_2txt[symb])
  134. LSUP = lambda letter: U('SUPERSCRIPT LATIN SMALL LETTER %s' % letter.upper())
  135. DSUP = lambda digit: U('SUPERSCRIPT %s' % digit_2txt[digit])
  136. SSUP = lambda symb: U('SUPERSCRIPT %s' % symb_2txt[symb])
  137. sub = {} # symb -> subscript symbol
  138. sup = {} # symb -> superscript symbol
  139. # latin subscripts
  140. for l in 'aeioruvxhklmnpst':
  141. sub[l] = LSUB(l)
  142. for l in 'in':
  143. sup[l] = LSUP(l)
  144. for gl in ['beta', 'gamma', 'rho', 'phi', 'chi']:
  145. sub[gl] = GSUB(gl)
  146. for d in [str(i) for i in range(10)]:
  147. sub[d] = DSUB(d)
  148. sup[d] = DSUP(d)
  149. for s in '+-=()':
  150. sub[s] = SSUB(s)
  151. sup[s] = SSUP(s)
  152. # Variable modifiers
  153. # TODO: Make brackets adjust to height of contents
  154. modifier_dict = {
  155. # Accents
  156. 'mathring': lambda s: center_accent(s, '\N{COMBINING RING ABOVE}'),
  157. 'ddddot': lambda s: center_accent(s, '\N{COMBINING FOUR DOTS ABOVE}'),
  158. 'dddot': lambda s: center_accent(s, '\N{COMBINING THREE DOTS ABOVE}'),
  159. 'ddot': lambda s: center_accent(s, '\N{COMBINING DIAERESIS}'),
  160. 'dot': lambda s: center_accent(s, '\N{COMBINING DOT ABOVE}'),
  161. 'check': lambda s: center_accent(s, '\N{COMBINING CARON}'),
  162. 'breve': lambda s: center_accent(s, '\N{COMBINING BREVE}'),
  163. 'acute': lambda s: center_accent(s, '\N{COMBINING ACUTE ACCENT}'),
  164. 'grave': lambda s: center_accent(s, '\N{COMBINING GRAVE ACCENT}'),
  165. 'tilde': lambda s: center_accent(s, '\N{COMBINING TILDE}'),
  166. 'hat': lambda s: center_accent(s, '\N{COMBINING CIRCUMFLEX ACCENT}'),
  167. 'bar': lambda s: center_accent(s, '\N{COMBINING OVERLINE}'),
  168. 'vec': lambda s: center_accent(s, '\N{COMBINING RIGHT ARROW ABOVE}'),
  169. 'prime': lambda s: s+'\N{PRIME}',
  170. 'prm': lambda s: s+'\N{PRIME}',
  171. # # Faces -- these are here for some compatibility with latex printing
  172. # 'bold': lambda s: s,
  173. # 'bm': lambda s: s,
  174. # 'cal': lambda s: s,
  175. # 'scr': lambda s: s,
  176. # 'frak': lambda s: s,
  177. # Brackets
  178. 'norm': lambda s: '\N{DOUBLE VERTICAL LINE}'+s+'\N{DOUBLE VERTICAL LINE}',
  179. 'avg': lambda s: '\N{MATHEMATICAL LEFT ANGLE BRACKET}'+s+'\N{MATHEMATICAL RIGHT ANGLE BRACKET}',
  180. 'abs': lambda s: '\N{VERTICAL LINE}'+s+'\N{VERTICAL LINE}',
  181. 'mag': lambda s: '\N{VERTICAL LINE}'+s+'\N{VERTICAL LINE}',
  182. }
  183. # VERTICAL OBJECTS
  184. HUP = lambda symb: U('%s UPPER HOOK' % symb_2txt[symb])
  185. CUP = lambda symb: U('%s UPPER CORNER' % symb_2txt[symb])
  186. MID = lambda symb: U('%s MIDDLE PIECE' % symb_2txt[symb])
  187. EXT = lambda symb: U('%s EXTENSION' % symb_2txt[symb])
  188. HLO = lambda symb: U('%s LOWER HOOK' % symb_2txt[symb])
  189. CLO = lambda symb: U('%s LOWER CORNER' % symb_2txt[symb])
  190. TOP = lambda symb: U('%s TOP' % symb_2txt[symb])
  191. BOT = lambda symb: U('%s BOTTOM' % symb_2txt[symb])
  192. # {} '(' -> (extension, start, end, middle) 1-character
  193. _xobj_unicode = {
  194. # vertical symbols
  195. # (( ext, top, bot, mid ), c1)
  196. '(': (( EXT('('), HUP('('), HLO('(') ), '('),
  197. ')': (( EXT(')'), HUP(')'), HLO(')') ), ')'),
  198. '[': (( EXT('['), CUP('['), CLO('[') ), '['),
  199. ']': (( EXT(']'), CUP(']'), CLO(']') ), ']'),
  200. '{': (( EXT('{}'), HUP('{'), HLO('{'), MID('{') ), '{'),
  201. '}': (( EXT('{}'), HUP('}'), HLO('}'), MID('}') ), '}'),
  202. '|': U('BOX DRAWINGS LIGHT VERTICAL'),
  203. 'Tee': U('BOX DRAWINGS LIGHT UP AND HORIZONTAL'),
  204. 'UpTack': U('BOX DRAWINGS LIGHT DOWN AND HORIZONTAL'),
  205. 'corner_up_centre'
  206. '(_ext': U('LEFT PARENTHESIS EXTENSION'),
  207. ')_ext': U('RIGHT PARENTHESIS EXTENSION'),
  208. '(_lower_hook': U('LEFT PARENTHESIS LOWER HOOK'),
  209. ')_lower_hook': U('RIGHT PARENTHESIS LOWER HOOK'),
  210. '(_upper_hook': U('LEFT PARENTHESIS UPPER HOOK'),
  211. ')_upper_hook': U('RIGHT PARENTHESIS UPPER HOOK'),
  212. '<': ((U('BOX DRAWINGS LIGHT VERTICAL'),
  213. U('BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT'),
  214. U('BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT')), '<'),
  215. '>': ((U('BOX DRAWINGS LIGHT VERTICAL'),
  216. U('BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT'),
  217. U('BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT')), '>'),
  218. 'lfloor': (( EXT('['), EXT('['), CLO('[') ), U('LEFT FLOOR')),
  219. 'rfloor': (( EXT(']'), EXT(']'), CLO(']') ), U('RIGHT FLOOR')),
  220. 'lceil': (( EXT('['), CUP('['), EXT('[') ), U('LEFT CEILING')),
  221. 'rceil': (( EXT(']'), CUP(']'), EXT(']') ), U('RIGHT CEILING')),
  222. 'int': (( EXT('int'), U('TOP HALF INTEGRAL'), U('BOTTOM HALF INTEGRAL') ), U('INTEGRAL')),
  223. 'sum': (( U('BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT'), '_', U('OVERLINE'), U('BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT')), U('N-ARY SUMMATION')),
  224. # horizontal objects
  225. #'-': '-',
  226. '-': U('BOX DRAWINGS LIGHT HORIZONTAL'),
  227. '_': U('LOW LINE'),
  228. # We used to use this, but LOW LINE looks better for roots, as it's a
  229. # little lower (i.e., it lines up with the / perfectly. But perhaps this
  230. # one would still be wanted for some cases?
  231. # '_': U('HORIZONTAL SCAN LINE-9'),
  232. # diagonal objects '\' & '/' ?
  233. '/': U('BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT'),
  234. '\\': U('BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT'),
  235. }
  236. _xobj_ascii = {
  237. # vertical symbols
  238. # (( ext, top, bot, mid ), c1)
  239. '(': (( '|', '/', '\\' ), '('),
  240. ')': (( '|', '\\', '/' ), ')'),
  241. # XXX this looks ugly
  242. # '[': (( '|', '-', '-' ), '['),
  243. # ']': (( '|', '-', '-' ), ']'),
  244. # XXX not so ugly :(
  245. '[': (( '[', '[', '[' ), '['),
  246. ']': (( ']', ']', ']' ), ']'),
  247. '{': (( '|', '/', '\\', '<' ), '{'),
  248. '}': (( '|', '\\', '/', '>' ), '}'),
  249. '|': '|',
  250. '<': (( '|', '/', '\\' ), '<'),
  251. '>': (( '|', '\\', '/' ), '>'),
  252. 'int': ( ' | ', ' /', '/ ' ),
  253. # horizontal objects
  254. '-': '-',
  255. '_': '_',
  256. # diagonal objects '\' & '/' ?
  257. '/': '/',
  258. '\\': '\\',
  259. }
  260. def xobj(symb, length):
  261. """Construct spatial object of given length.
  262. return: [] of equal-length strings
  263. """
  264. if length <= 0:
  265. raise ValueError("Length should be greater than 0")
  266. # TODO robustify when no unicodedat available
  267. if _use_unicode:
  268. _xobj = _xobj_unicode
  269. else:
  270. _xobj = _xobj_ascii
  271. vinfo = _xobj[symb]
  272. c1 = top = bot = mid = None
  273. if not isinstance(vinfo, tuple): # 1 entry
  274. ext = vinfo
  275. else:
  276. if isinstance(vinfo[0], tuple): # (vlong), c1
  277. vlong = vinfo[0]
  278. c1 = vinfo[1]
  279. else: # (vlong), c1
  280. vlong = vinfo
  281. ext = vlong[0]
  282. try:
  283. top = vlong[1]
  284. bot = vlong[2]
  285. mid = vlong[3]
  286. except IndexError:
  287. pass
  288. if c1 is None:
  289. c1 = ext
  290. if top is None:
  291. top = ext
  292. if bot is None:
  293. bot = ext
  294. if mid is not None:
  295. if (length % 2) == 0:
  296. # even height, but we have to print it somehow anyway...
  297. # XXX is it ok?
  298. length += 1
  299. else:
  300. mid = ext
  301. if length == 1:
  302. return c1
  303. res = []
  304. next = (length - 2)//2
  305. nmid = (length - 2) - next*2
  306. res += [top]
  307. res += [ext]*next
  308. res += [mid]*nmid
  309. res += [ext]*next
  310. res += [bot]
  311. return res
  312. def vobj(symb, height):
  313. """Construct vertical object of a given height
  314. see: xobj
  315. """
  316. return '\n'.join( xobj(symb, height) )
  317. def hobj(symb, width):
  318. """Construct horizontal object of a given width
  319. see: xobj
  320. """
  321. return ''.join( xobj(symb, width) )
  322. # RADICAL
  323. # n -> symbol
  324. root = {
  325. 2: U('SQUARE ROOT'), # U('RADICAL SYMBOL BOTTOM')
  326. 3: U('CUBE ROOT'),
  327. 4: U('FOURTH ROOT'),
  328. }
  329. # RATIONAL
  330. VF = lambda txt: U('VULGAR FRACTION %s' % txt)
  331. # (p,q) -> symbol
  332. frac = {
  333. (1, 2): VF('ONE HALF'),
  334. (1, 3): VF('ONE THIRD'),
  335. (2, 3): VF('TWO THIRDS'),
  336. (1, 4): VF('ONE QUARTER'),
  337. (3, 4): VF('THREE QUARTERS'),
  338. (1, 5): VF('ONE FIFTH'),
  339. (2, 5): VF('TWO FIFTHS'),
  340. (3, 5): VF('THREE FIFTHS'),
  341. (4, 5): VF('FOUR FIFTHS'),
  342. (1, 6): VF('ONE SIXTH'),
  343. (5, 6): VF('FIVE SIXTHS'),
  344. (1, 8): VF('ONE EIGHTH'),
  345. (3, 8): VF('THREE EIGHTHS'),
  346. (5, 8): VF('FIVE EIGHTHS'),
  347. (7, 8): VF('SEVEN EIGHTHS'),
  348. }
  349. # atom symbols
  350. _xsym = {
  351. '==': ('=', '='),
  352. '<': ('<', '<'),
  353. '>': ('>', '>'),
  354. '<=': ('<=', U('LESS-THAN OR EQUAL TO')),
  355. '>=': ('>=', U('GREATER-THAN OR EQUAL TO')),
  356. '!=': ('!=', U('NOT EQUAL TO')),
  357. ':=': (':=', ':='),
  358. '+=': ('+=', '+='),
  359. '-=': ('-=', '-='),
  360. '*=': ('*=', '*='),
  361. '/=': ('/=', '/='),
  362. '%=': ('%=', '%='),
  363. '*': ('*', U('DOT OPERATOR')),
  364. '-->': ('-->', U('EM DASH') + U('EM DASH') +
  365. U('BLACK RIGHT-POINTING TRIANGLE') if U('EM DASH')
  366. and U('BLACK RIGHT-POINTING TRIANGLE') else None),
  367. '==>': ('==>', U('BOX DRAWINGS DOUBLE HORIZONTAL') +
  368. U('BOX DRAWINGS DOUBLE HORIZONTAL') +
  369. U('BLACK RIGHT-POINTING TRIANGLE') if
  370. U('BOX DRAWINGS DOUBLE HORIZONTAL') and
  371. U('BOX DRAWINGS DOUBLE HORIZONTAL') and
  372. U('BLACK RIGHT-POINTING TRIANGLE') else None),
  373. '.': ('*', U('RING OPERATOR')),
  374. }
  375. def xsym(sym):
  376. """get symbology for a 'character'"""
  377. op = _xsym[sym]
  378. if _use_unicode:
  379. return op[1]
  380. else:
  381. return op[0]
  382. # SYMBOLS
  383. atoms_table = {
  384. # class how-to-display
  385. 'Exp1': U('SCRIPT SMALL E'),
  386. 'Pi': U('GREEK SMALL LETTER PI'),
  387. 'Infinity': U('INFINITY'),
  388. 'NegativeInfinity': U('INFINITY') and ('-' + U('INFINITY')), # XXX what to do here
  389. #'ImaginaryUnit': U('GREEK SMALL LETTER IOTA'),
  390. #'ImaginaryUnit': U('MATHEMATICAL ITALIC SMALL I'),
  391. 'ImaginaryUnit': U('DOUBLE-STRUCK ITALIC SMALL I'),
  392. 'EmptySet': U('EMPTY SET'),
  393. 'Naturals': U('DOUBLE-STRUCK CAPITAL N'),
  394. 'Naturals0': (U('DOUBLE-STRUCK CAPITAL N') and
  395. (U('DOUBLE-STRUCK CAPITAL N') +
  396. U('SUBSCRIPT ZERO'))),
  397. 'Integers': U('DOUBLE-STRUCK CAPITAL Z'),
  398. 'Rationals': U('DOUBLE-STRUCK CAPITAL Q'),
  399. 'Reals': U('DOUBLE-STRUCK CAPITAL R'),
  400. 'Complexes': U('DOUBLE-STRUCK CAPITAL C'),
  401. 'Universe': U('MATHEMATICAL DOUBLE-STRUCK CAPITAL U'),
  402. 'IdentityMatrix': U('MATHEMATICAL DOUBLE-STRUCK CAPITAL I'),
  403. 'ZeroMatrix': U('MATHEMATICAL DOUBLE-STRUCK DIGIT ZERO'),
  404. 'OneMatrix': U('MATHEMATICAL DOUBLE-STRUCK DIGIT ONE'),
  405. 'Differential': U('DOUBLE-STRUCK ITALIC SMALL D'),
  406. 'Union': U('UNION'),
  407. 'ElementOf': U('ELEMENT OF'),
  408. 'SmallElementOf': U('SMALL ELEMENT OF'),
  409. 'SymmetricDifference': U('INCREMENT'),
  410. 'Intersection': U('INTERSECTION'),
  411. 'Ring': U('RING OPERATOR'),
  412. 'Multiplication': U('MULTIPLICATION SIGN'),
  413. 'TensorProduct': U('N-ARY CIRCLED TIMES OPERATOR'),
  414. 'Dots': U('HORIZONTAL ELLIPSIS'),
  415. 'Modifier Letter Low Ring':U('Modifier Letter Low Ring'),
  416. 'EmptySequence': 'EmptySequence',
  417. 'SuperscriptPlus': U('SUPERSCRIPT PLUS SIGN'),
  418. 'SuperscriptMinus': U('SUPERSCRIPT MINUS'),
  419. 'Dagger': U('DAGGER'),
  420. 'Degree': U('DEGREE SIGN'),
  421. #Logic Symbols
  422. 'And': U('LOGICAL AND'),
  423. 'Or': U('LOGICAL OR'),
  424. 'Not': U('NOT SIGN'),
  425. 'Nor': U('NOR'),
  426. 'Nand': U('NAND'),
  427. 'Xor': U('XOR'),
  428. 'Equiv': U('LEFT RIGHT DOUBLE ARROW'),
  429. 'NotEquiv': U('LEFT RIGHT DOUBLE ARROW WITH STROKE'),
  430. 'Implies': U('LEFT RIGHT DOUBLE ARROW'),
  431. 'NotImplies': U('LEFT RIGHT DOUBLE ARROW WITH STROKE'),
  432. 'Arrow': U('RIGHTWARDS ARROW'),
  433. 'ArrowFromBar': U('RIGHTWARDS ARROW FROM BAR'),
  434. 'NotArrow': U('RIGHTWARDS ARROW WITH STROKE'),
  435. 'Tautology': U('BOX DRAWINGS LIGHT UP AND HORIZONTAL'),
  436. 'Contradiction': U('BOX DRAWINGS LIGHT DOWN AND HORIZONTAL')
  437. }
  438. def pretty_atom(atom_name, default=None, printer=None):
  439. """return pretty representation of an atom"""
  440. if _use_unicode:
  441. if printer is not None and atom_name == 'ImaginaryUnit' and printer._settings['imaginary_unit'] == 'j':
  442. return U('DOUBLE-STRUCK ITALIC SMALL J')
  443. else:
  444. return atoms_table[atom_name]
  445. else:
  446. if default is not None:
  447. return default
  448. raise KeyError('only unicode') # send it default printer
  449. def pretty_symbol(symb_name, bold_name=False):
  450. """return pretty representation of a symbol"""
  451. # let's split symb_name into symbol + index
  452. # UC: beta1
  453. # UC: f_beta
  454. if not _use_unicode:
  455. return symb_name
  456. name, sups, subs = split_super_sub(symb_name)
  457. def translate(s, bold_name) :
  458. if bold_name:
  459. gG = greek_bold_unicode.get(s)
  460. else:
  461. gG = greek_unicode.get(s)
  462. if gG is not None:
  463. return gG
  464. for key in sorted(modifier_dict.keys(), key=lambda k:len(k), reverse=True) :
  465. if s.lower().endswith(key) and len(s)>len(key):
  466. return modifier_dict[key](translate(s[:-len(key)], bold_name))
  467. if bold_name:
  468. return ''.join([bold_unicode[c] for c in s])
  469. return s
  470. name = translate(name, bold_name)
  471. # Let's prettify sups/subs. If it fails at one of them, pretty sups/subs are
  472. # not used at all.
  473. def pretty_list(l, mapping):
  474. result = []
  475. for s in l:
  476. pretty = mapping.get(s)
  477. if pretty is None:
  478. try: # match by separate characters
  479. pretty = ''.join([mapping[c] for c in s])
  480. except (TypeError, KeyError):
  481. return None
  482. result.append(pretty)
  483. return result
  484. pretty_sups = pretty_list(sups, sup)
  485. if pretty_sups is not None:
  486. pretty_subs = pretty_list(subs, sub)
  487. else:
  488. pretty_subs = None
  489. # glue the results into one string
  490. if pretty_subs is None: # nice formatting of sups/subs did not work
  491. if subs:
  492. name += '_'+'_'.join([translate(s, bold_name) for s in subs])
  493. if sups:
  494. name += '__'+'__'.join([translate(s, bold_name) for s in sups])
  495. return name
  496. else:
  497. sups_result = ' '.join(pretty_sups)
  498. subs_result = ' '.join(pretty_subs)
  499. return ''.join([name, sups_result, subs_result])
  500. def annotated(letter):
  501. """
  502. Return a stylised drawing of the letter ``letter``, together with
  503. information on how to put annotations (super- and subscripts to the
  504. left and to the right) on it.
  505. See pretty.py functions _print_meijerg, _print_hyper on how to use this
  506. information.
  507. """
  508. ucode_pics = {
  509. 'F': (2, 0, 2, 0, '\N{BOX DRAWINGS LIGHT DOWN AND RIGHT}\N{BOX DRAWINGS LIGHT HORIZONTAL}\n'
  510. '\N{BOX DRAWINGS LIGHT VERTICAL AND RIGHT}\N{BOX DRAWINGS LIGHT HORIZONTAL}\n'
  511. '\N{BOX DRAWINGS LIGHT UP}'),
  512. 'G': (3, 0, 3, 1, '\N{BOX DRAWINGS LIGHT ARC DOWN AND RIGHT}\N{BOX DRAWINGS LIGHT HORIZONTAL}\N{BOX DRAWINGS LIGHT ARC DOWN AND LEFT}\n'
  513. '\N{BOX DRAWINGS LIGHT VERTICAL}\N{BOX DRAWINGS LIGHT RIGHT}\N{BOX DRAWINGS LIGHT DOWN AND LEFT}\n'
  514. '\N{BOX DRAWINGS LIGHT ARC UP AND RIGHT}\N{BOX DRAWINGS LIGHT HORIZONTAL}\N{BOX DRAWINGS LIGHT ARC UP AND LEFT}')
  515. }
  516. ascii_pics = {
  517. 'F': (3, 0, 3, 0, ' _\n|_\n|\n'),
  518. 'G': (3, 0, 3, 1, ' __\n/__\n\\_|')
  519. }
  520. if _use_unicode:
  521. return ucode_pics[letter]
  522. else:
  523. return ascii_pics[letter]
  524. _remove_combining = dict.fromkeys(list(range(ord('\N{COMBINING GRAVE ACCENT}'), ord('\N{COMBINING LATIN SMALL LETTER X}')))
  525. + list(range(ord('\N{COMBINING LEFT HARPOON ABOVE}'), ord('\N{COMBINING ASTERISK ABOVE}'))))
  526. def is_combining(sym):
  527. """Check whether symbol is a unicode modifier. """
  528. return ord(sym) in _remove_combining
  529. def center_accent(string, accent):
  530. """
  531. Returns a string with accent inserted on the middle character. Useful to
  532. put combining accents on symbol names, including multi-character names.
  533. Parameters
  534. ==========
  535. string : string
  536. The string to place the accent in.
  537. accent : string
  538. The combining accent to insert
  539. References
  540. ==========
  541. .. [1] https://en.wikipedia.org/wiki/Combining_character
  542. .. [2] https://en.wikipedia.org/wiki/Combining_Diacritical_Marks
  543. """
  544. # Accent is placed on the previous character, although it may not always look
  545. # like that depending on console
  546. midpoint = len(string) // 2 + 1
  547. firstpart = string[:midpoint]
  548. secondpart = string[midpoint:]
  549. return firstpart + accent + secondpart
  550. def line_width(line):
  551. """Unicode combining symbols (modifiers) are not ever displayed as
  552. separate symbols and thus should not be counted
  553. """
  554. return len(line.translate(_remove_combining))
  555. def is_subscriptable_in_unicode(subscript):
  556. """
  557. Checks whether a string is subscriptable in unicode or not.
  558. Parameters
  559. ==========
  560. subscript: the string which needs to be checked
  561. Examples
  562. ========
  563. >>> from sympy.printing.pretty.pretty_symbology import is_subscriptable_in_unicode
  564. >>> is_subscriptable_in_unicode('abc')
  565. False
  566. >>> is_subscriptable_in_unicode('123')
  567. True
  568. """
  569. return all(character in sub for character in subscript)
  570. def center_pad(wstring, wtarget, fillchar=' '):
  571. """
  572. Return the padding strings necessary to center a string of
  573. wstring characters wide in a wtarget wide space.
  574. The line_width wstring should always be less or equal to wtarget
  575. or else a ValueError will be raised.
  576. """
  577. if wstring > wtarget:
  578. raise ValueError('not enough space for string')
  579. wdelta = wtarget - wstring
  580. wleft = wdelta // 2 # favor left '1 '
  581. wright = wdelta - wleft
  582. left = fillchar * wleft
  583. right = fillchar * wright
  584. return left, right
  585. def center(string, width, fillchar=' '):
  586. """Return a centered string of length determined by `line_width`
  587. that uses `fillchar` for padding.
  588. """
  589. left, right = center_pad(line_width(string), width, fillchar)
  590. return ''.join([left, string, right])