From de3440ab223b0031d10e447d124c5f68cbb388cd Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Mon, 4 May 2015 03:38:21 +0200 Subject: [PATCH 01/12] initial implementation of unicode art --- src/sage/all.py | 1 + src/sage/misc/all.py | 1 - src/sage/misc/ascii_art.py | 970 +--------------------- src/sage/structure/sage_object.pyx | 82 +- src/sage/typeset/__init__.py | 0 src/sage/typeset/all.py | 4 + src/sage/typeset/ascii_art.py | 242 ++++++ src/sage/typeset/character_art.py | 722 ++++++++++++++++ src/sage/typeset/character_art_factory.py | 324 ++++++++ src/sage/typeset/symbols.py | 340 ++++++++ src/sage/typeset/unicode_art.py | 112 +++ 11 files changed, 1825 insertions(+), 973 deletions(-) create mode 100644 src/sage/typeset/__init__.py create mode 100644 src/sage/typeset/all.py create mode 100644 src/sage/typeset/ascii_art.py create mode 100644 src/sage/typeset/character_art.py create mode 100644 src/sage/typeset/character_art_factory.py create mode 100644 src/sage/typeset/symbols.py create mode 100644 src/sage/typeset/unicode_art.py diff --git a/src/sage/all.py b/src/sage/all.py index d2494e0e262..6285971dcf3 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -82,6 +82,7 @@ import sage.misc.lazy_import from sage.misc.all import * # takes a while +from sage.typeset.all import * from sage.repl.all import * from sage.misc.sh import sh diff --git a/src/sage/misc/all.py b/src/sage/misc/all.py index a459fb3aed6..268129f870c 100644 --- a/src/sage/misc/all.py +++ b/src/sage/misc/all.py @@ -200,4 +200,3 @@ def _latex_(self): lazy_import("sage.misc", "messaging", deprecation=18140) -from ascii_art import ascii_art diff --git a/src/sage/misc/ascii_art.py b/src/sage/misc/ascii_art.py index 2c5efd07187..ba80f1c177f 100644 --- a/src/sage/misc/ascii_art.py +++ b/src/sage/misc/ascii_art.py @@ -1,969 +1,7 @@ -# -*- coding: utf-8 -*- -r""" -ASCII Art - -This file contains: - -- :class:`AsciiArt` an simple implementation of an ASCII art object, -- :func:`ascii_art` a function to get the ASCII art representation of any - object in Sage, -- several others functions use to get ASCII art representation of primitive - python elements (list, tuple, dict, set). - -AUTHOR: - -- Jean-Baptiste Priez (2013-04): initial version - -EXAMPLES:: - - sage: n = var('n') - sage: integrate(n^2/x,x) - n^2*log(x) - sage: ascii_art(integrate(n^2/x,x)) - 2 - n *log(x) - sage: ascii_art(integrate(n^2/(pi*x),x)) - 2 - n *log(x) - --------- - pi - sage: ascii_art(list(Partitions(6))) - [ * ] - [ ** * ] - [ *** ** * * ] - [ **** *** * ** ** * * ] - [ ***** **** * *** ** * ** * * * ] - [ ******, * , ** , * , ***, * , * , **, * , * , * ] - -This method :meth:`ascii_art` could be automatically use by the display hook -manager activated by the magic function: ``%display ascii_art``:: - - sage: from sage.repl.interpreter import get_test_shell - sage: shell = get_test_shell() - sage: shell.run_cell('%display ascii_art') - sage: shell.run_cell("i = var('i')") - sage: shell.run_cell('sum(factorial(i)*x^i, i, 0, 10)') - 10 9 8 7 6 5 4 3 - 3628800*x + 362880*x + 40320*x + 5040*x + 720*x + 120*x + 24*x + 6*x - - 2 - + 2*x + x + 1 - sage: shell.run_cell('3/(7*x)') - 3 - --- - 7*x - sage: shell.run_cell('list(Compositions(5))') - [ * - [ * ** * * * - [ * * ** *** * ** * * ** * * - [ * * * * ** ** *** **** * * ** *** * ** * - [ *, * , * , * , * , * , * , * , **, ** , ** , ** , ***, *** , ****, - - ] - ] - ] - ] - ***** ] - sage: shell.run_cell('%display simple') - sage: shell.quit() - -:: - - . , , ... - .? .~?$NNO.II7. ..GOGG - ., ~7NNI7NDG$ ...~~~MGG - ..:IG.. ...G7:DDGNNDO.. ...7:~~~~GNN - .~~GGGGG... .O .=$+OD7GI$ ...GG:~+~~~~MG?M.7? - .~~~D~~GGGGDGOM$..~+IN=NDG.G::D~~~?~~~7?NDI:???G - .~~~~G:~~G~D:~NONGMMOGD$ND~~::I77~~$?++MN7G GI?D. - 7+I~~DG~~~N:GNGGOD$7OIOOMII::~:I7?G+OGN7G $III?. - .7==I~~~++IN?+++?$7ND$==$G:G??????N$OG$ ~III?7 - .$$+++?GG=+G?GDM?GOG:NGMGN7??????D$D~GODIII???. - D++++NG+DD$+=GGONGI$DNGDN??????,IN=DOGODN???. - , +~+++N??D$I+I=GNMDGDGNIINN??D+:::O~$GGGDIG?7 - :DD.:::OG?G=I~~G7GNM7777NIGODDNNG:::I$GGGGG7??O. - ~GDDM:::GGMG+?+~$NNN$7IG7NMNMMMN, =::I7GGGGGO???~ - :GDND7?::OOM.D+O~GGDI77777DMGNM$. ~:,$IGGGGGO???DO. - OGDDNN.D77OO. $?7==GG777$7GNGMGO. NOIGGGGGO???G$. - .OODNNN,DIGDM$GGGGG==GGGGIGNDMDMG, IGIGGGDON???GG: - .GODNDM.G$I$IOIOMG$G$?GGGIO,7OD7GG. ,GDIGGG??????GGG. - .DGDNI7I7MDI+OOODN$$O,$7DMIN,,IOO77O. G?$DIGGG?ID???OGG - GGDNNMMO7GD+OOOGIMOG7::NN====:?MMNGIDD,..IINIGGG?I??DIOGG? - .7ODMMMN.G7IOGOOODIMG,,:::$=~==::7OGG~IGOMDGMNIGGG????.OG$G. - ?ODDMNNNO,II$OGODMGDMM?:DMG==~MDINNM$.7$IONDGI?GGG????:$GGG. - .$MDMNNNNN..:?7GDDDGG,GGM?~:GGGDNND.GIM7D+GI$ON.:?GGG????$$OGG. - .7DNDDDNMDOGG.=IGGND=7II+N??::$GIIO,IIGMG?7I7G$ON?,IGGG????7GIGG. - ~GGGDNMMOOGGG$MGMMGDGMDGM?,G:GNG,:IIIGDG7IGGGGG$+NMIGGO?????IGGG. - .GGGDDMM7OGNGMODMNDDDOO.MII?GI$7IIIIING7GGDMM.IGDG.G7GGGG??$?7GGG - .IGDDDDOINNGMGMDMNDDGDO...$OI+??7OIIIDDGN+$==I=GD ?,NGGGGN7????GGG - 7GGNDM$GONDGDD$MMGNDN. G.:$$G$?$7II$GOO,O=+7O7O~N?OM?GGGGGD+??$GG - OOGDOI7DGMG..=~DG$DD. $,,$$$D??7ODOOOODG$777$G OGMM:GGGGGG??GGG. - .$GDDG?7DMOGDNNMGGDGG ..,=O7$GG$+O$$OG=+O:GI77G$. ...DIGGGGI?$GGG. - .OGGGGO7OGDGGNGGGDGG. O:,77$O$$$D $GN:7777GD ..$GGGG??GGG$. - .GGGGDI GD.~NOGDGGG MG,777G77D7 +$~D77777= ..7GGG???GGO. - GGD. .~NODN... ..D::77O$7G77 .OG:G???G7. 7$NOM??GG=. - . . . .+~~DD~77$G7OD7 .?GGO?$OM$D ?????DGG... - 7I77DN$II7$M.G$G . .:G?==$G .?????D??~ - . .:IIIGO7O$GN..O$ 7I=~+?I. =???7? GGIG - $,III7NGGNNNG, .O. O====7I. .DG??$?. GG?G - .$7III$77777$$~ . +====D$GG ~$???: .$GIG. - :.+IIII$77$G$$O,+ OI==+7I$7=.:??? .GG$. - ,,III$I7?I7$$OG7D ?+,==+77G?$,???. GG? - ?IIIII=+$I77777 .:++=+++O77:,??: GG. - .IIIIG+III77777. 7,=,++?II7$,?G .GO . - $OII7?OIIIIIO .I:++??IIIII???. I$ . - .DNGDMOIG777. +~++?I$IIMN?, .G, - .G.I?IIDGII~ .OG??$$MIMGI. G, - N+?II7OGI7. DD?$GGDGID. : - =?IIOOI$. ?~~GGG$II .+. - ,7=IINGI ?=IGGGI?. - G,+IGOII ?+II7??+ - .,:?IGOII ?+GIOII - .:N+=?IMGI7=. .~:$I?IO. - .$:IGO?$IIIII7+. O::I7I?. - .:::=IIGIIIIIII. .~,$IG?IO - .+:$IIIIMIII7G$?. I::I7IIG7 - .$I$+7IIIMMMG7I7 ?G,DGGNII. - .$~:$IIGMNGND77I: +I$GOD=?= - ?=?IIIIGIIIINI7777? .7~=~===?. - ONGNDG??IG?III$N7I777 D~====I - .:$??7IIIIIII.....,.... O::==~$D. - .,........ .. ..M:I.==$7G. - I?::IIIII7. - .~:G:IIIIIG. - .$:,O7III$$O - ::~DOGGNNO - .::,IOODI, - .7????$. - ... . """ -#******************************************************************************* -# Copyright (C) 2013 Jean-Baptiste Priez , -# -# Distributed under the terms of the GNU General Public License (GPL) -# -# This code is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# The full text of the GPL is available at: -# -# http://www.gnu.org/licenses/ -#******************************************************************************* - -import os, sys -from sage.structure.sage_object import SageObject -from sage.rings.integer import Integer - - -################################################################################ -### Global variable use to compute the maximal length allows for ascii art -### object. -MAX_WIDTH = None -################################################################################ - -class AsciiArt(SageObject): - r""" - An Ascii art object is an object with some specific representation for - *printing*. - - INPUT: - - - ``lines`` -- the list of lines of the representation of the ascii art - object - - ``breakpoints`` -- the list of points where the representation can be - split - - ``baseline`` -- the reference line (from the bottom) - - ``atomic`` -- indicate if the ascii art representation is splittable - (must be coherent with breakpoints) - - EXAMPLES:: - - sage: i = var('i') - sage: ascii_art(sum(pi^i/factorial(i)*x^i, i, 0, oo)) - pi*x - e - """ - def __init__(self, lines=[], breakpoints=[], baseline=None, atomic=True): - r""" - TESTS:: - - sage: from sage.misc.ascii_art import AsciiArt - sage: aao = AsciiArt() - sage: aao.is_atomic() - True - sage: aao - - sage: aa = AsciiArt([" * ", " * * ", "*****"]); aa - * - * * - ***** - """ - self._is_uniq = True - self._matrix = lines - self._breakpoints = breakpoints - self._baseline = baseline if baseline is not None else 0 - self._is_atomic = atomic - - self._h = len(lines) - self._l = max(map(lambda line: len(line), lines) + [0]) - - def __getitem__(self, key): - r""" - Return the line `key` of the ASCII art object. - - TESTS:: - - sage: from sage.misc.ascii_art import AsciiArt - sage: p5 = AsciiArt([" * ", " * * ", "*****"]) - sage: p5[1] - ' * * ' - """ - return self._matrix[key] - - def __iter__(self): - r""" - Iterator on all lines of the ASCII art object. - - TESTS:: - - sage: from sage.misc.ascii_art import AsciiArt - sage: p5 = AsciiArt([" * ", " * * ", "*****"]) - sage: for line in p5: - ....: print line - * - * * - ***** - """ - for elem in self._matrix: - yield elem - - def _repr_(self): - r""" - TESTS:: - - sage: from sage.misc.ascii_art import AsciiArt - sage: p5 = AsciiArt([" * ", " * * ", "*****"]) - sage: repr(p5) - ' * \n * * \n*****' - """ - # Compute the max length of a draw - global MAX_WIDTH - if MAX_WIDTH is not None: - hsize = MAX_WIDTH - else: - hsize = self._terminal_width() - ######### - # if the draw is larger than the max length it try to split... - if hsize <= self._l and len(self._breakpoints) > 0: - return self._split_repr_(hsize) - ######### - output = "" - if len(self._matrix) > 0: - for i in range(len(self._matrix) - 1): - output += self._matrix[i] + "\n" - return output + self._matrix[len(self._matrix) - 1] - return output - - - def is_atomic(self): - r""" - Return ``True`` if the :class:`AsciiArt` object is not splitable and - ``False`` in otherwise. - - For example, we considere a linear expression:: - - sage: a = 14*x^5 + 5*x^4 - sage: ascii_art(a) - 5 4 - 14*x + 5*x - - If ASCII art object is not atomic, it is splittable on the ``+`` - (in fact it is not really true because we use ``sympy`` to make - ASCII art). - - TESTS:: - - sage: from sage.misc.ascii_art import AsciiArt - sage: aa = AsciiArt([" * ", " * * ", "*****"]); aa - * - * * - ***** - sage: aa.is_atomic() - True - sage: laa = ascii_art([aa,aa]) - sage: laa.is_atomic() - False - """ - return self._is_atomic - - def get_baseline(self): - r""" - Return the line where the baseline is, for example:: - - 5 4 - 14*x + 5*x - - the baseline has at line `0` and :: - - { o } - { \ : 4 } - { o } - - has at line `1`. - - TESTS:: - - sage: from sage.misc.ascii_art import AsciiArt - sage: aa = AsciiArt([" * ", " * * ", " * * ", "*******"], baseline=1);aa - * - * * - * * - ******* - sage: aa.get_baseline() - 1 - sage: b = AsciiArt(["<-"]) - sage: aa+b - * - * * - * * <- - ******* - """ - return self._baseline - - def get_breakpoints(self): - r""" - Return an iterator of breakpoints where the object can be split. - - For example the expression:: - - 5 4 - 14x + 5x - - can be split on position 4 (on the ``+``). - - EXAMPLES:: - - sage: from sage.misc.ascii_art import AsciiArt - sage: p3 = AsciiArt([" * ", "***"]) - sage: p5 = AsciiArt([" * ", " * * ", "*****"]) - sage: aa = ascii_art([p3, p5]) - sage: aa.get_breakpoints() - [2, 5, 6, 7, 12] - """ - return self._breakpoints - - def _isatty(self): - """ - Test whether stdout is a TTY - - If this test succeeds, you can assume that stdout is directly - connected to a terminal. Otherwise you should treat stdout as - being redirected to a file. - - OUTPUT: - - Boolean - - EXAMPLES:: - - sage: from sage.misc.ascii_art import empty_ascii_art - sage: empty_ascii_art._isatty() - False - """ - from sage.doctest import DOCTEST_MODE - if DOCTEST_MODE: - return False - try: - return os.isatty(sys.stdout.fileno()) - except Exception: - # The IPython zeromq kernel uses a fake stdout that does - # not support fileno() - return False - - def _terminal_width(self): - """ - Compute the width size of the terminal. - - EXAMPLES:: - - sage: from sage.misc.ascii_art import empty_ascii_art - sage: empty_ascii_art._terminal_width() - 80 - """ - if not self._isatty(): - return 80 - import fcntl, termios, struct - rc = fcntl.ioctl(int(0), termios.TIOCGWINSZ, - struct.pack('HHHH', sys.stdout.fileno(), 0, 0, 0)) - h, w, hp, wp = struct.unpack('HHHH', rc) - return w - - def _split_repr_(self, size): - r""" - Split the draw and the left part has length ``size``. - - TESTS:: +Temporary redirecton - sage: from sage.misc.ascii_art import AsciiArt - sage: p3 = AsciiArt([" * ", "***"]) - sage: p5 = AsciiArt([" * ", " * * ", "*****"]) - sage: aa = ascii_art([p3, p5]) - sage: print aa._split_repr_(6) - [ - [ * - [ *** - - * ] - * * ] - , ***** ] - """ - import sys - f_split = self._breakpoints[0]; i = 1 - while i < len(self._breakpoints) and self._breakpoints[i] < size: - f_split = self._breakpoints[i] - i += 1 - if size <= f_split: - import warnings - warnings.warn("the console size is smaller than the pretty" + - "representation of the object") - top, bottom = self.split(f_split) - return (top * AsciiArt([""])).__repr__() + "\n" + bottom.__repr__() - - def split(self, pos): - r""" - Split the representation at the position ``pos``. - - EXMAPLES:: - - sage: from sage.misc.ascii_art import AsciiArt - sage: p3 = AsciiArt([" * ", "***"]) - sage: p5 = AsciiArt([" * ", " * * ", "*****"]) - sage: aa = ascii_art([p3, p5]) - sage: a,b= aa.split(6) - sage: a - [ - [ * - [ ***, - sage: b - * ] - * * ] - ***** ] - """ - left = []; right = [] - for line in self: - left.append(line[:pos]) - right.append(line[pos:]) - l_bp = []; r_bp = [] - for bp in self._breakpoints: - if bp < pos: - l_bp.append(bp) - elif bp > pos: - r_bp.append(bp - pos) - return AsciiArt(left, l_bp), AsciiArt(right, r_bp) - - @staticmethod - def _compute_new_baseline(obj1, obj2): - r""" - TESTS:: - - sage: from sage.misc.ascii_art import AsciiArt - sage: l5 = AsciiArt(lines = ['|' for _ in range(5)], baseline = 2); l5 - | - | - | - | - | - sage: l3 = AsciiArt(lines = ['|' for _ in range(3)], baseline = 1); l3 - | - | - | - sage: AsciiArt._compute_new_baseline(l5, l3) - 2 - sage: l5 + l3 - | - || - || - || - | - sage: l5._baseline = 0 - sage: AsciiArt._compute_new_baseline(l5, l3) - 1 - sage: l5 + l3 - | - | - | - || - || - | - sage: l5._baseline = 4 - sage: AsciiArt._compute_new_baseline(l5, l3) - 4 - sage: l5 + l3 - | - || - || - | - | - | - sage: l3._baseline = 0 - sage: AsciiArt._compute_new_baseline(l3, l5) - 4 - sage: l3 + l5 - | - | - || - | - | - | - | - sage: l5._baseline = None - sage: AsciiArt._compute_new_baseline(l3, l5) - 2 - sage: l3._baseline = 2 - sage: AsciiArt._compute_new_baseline(l3, l5) - 4 - sage: l3 + l5 - || - || - || - | - | - - """ - if obj1.get_baseline() is None: - if obj2.get_baseline() is None: - return None - return obj2.get_baseline() + max(obj1._h - obj2._h, 0) - if obj2.get_baseline() is None: - return obj1.get_baseline() + max(obj2._h - obj1._h, 0) - return max( - obj1.get_baseline(), - obj2.get_baseline() - ) - - @staticmethod - def _compute_new_h(obj1, obj2): - r""" - TESTS:: - - sage: from sage.misc.ascii_art import AsciiArt - sage: l5 = AsciiArt(lines=['|' for _ in range(5)], baseline=2); l5 - | - | - | - | - | - sage: l3 = AsciiArt(lines=['|' for _ in range(3)], baseline=1); l3 - | - | - | - sage: AsciiArt._compute_new_h(l5, l3) - 5 - sage: l5 + l3 - | - || - || - || - | - sage: l5._baseline = 0 - sage: AsciiArt._compute_new_h(l5, l3) - 6 - sage: l5 + l3 - | - | - | - || - || - | - sage: l5._baseline = 4 - sage: AsciiArt._compute_new_h(l5, l3) - 6 - sage: l5 + l3 - | - || - || - | - | - | - sage: l3._baseline = 0 - sage: AsciiArt._compute_new_h(l3, l5) - 7 - sage: l3 + l5 - | - | - || - | - | - | - | - """ - if obj1.get_baseline() is None or obj2.get_baseline() is None: - return max(obj1._h, obj2._h) - return max( - obj1.get_baseline(), - obj2.get_baseline() - ) + max( - obj1._h - obj1.get_baseline(), - obj2._h - obj2.get_baseline() - ) - - def __len__(self): - r""" - Return the length of the ASCII art object. - - TESTS:: - - sage: from sage.misc.ascii_art import AsciiArt - sage: p3 = AsciiArt([" * ", "***"]) - sage: len(p3) - 3 - sage: p5 = AsciiArt([" * ", " * * ", "*****"]) - sage: len(p5) - 5 - """ - return self._l - - def __add__(self, Nelt): - r""" - Concatenate two ascii art object. - - By default, when two object are concatenated, the new one will be - splittable between both. - - If the baseline is defined, the concatenation is computed such that the - new baseline coincidate with the olders. - - For example, let `T` be a tree with it's baseline ascii art - representation in the middle:: - - o - \ - o - / \ - o o - - and let `M` be a matrix with it's baseline ascii art representation at - the middle two:: - - [1 2 3] - [4 5 6] - [7 8 9] - - then the concatenation of both will give:: - - o - \ [1 2 3] - o [4 5 6] - / \ [7 8 9] - o o - - If one of the objects has not baseline, the concatenation is realized - from the top:: - - o [1 2 3] - \ [4 5 6] - o [7 8 9] - / \ - o o - - TESTS:: - - sage: from sage.misc.ascii_art import AsciiArt - sage: l5 = AsciiArt(lines=['|' for _ in range(5)], baseline=2); l5 - | - | - | - | - | - sage: l3 = AsciiArt(lines=['|' for _ in range(3)], baseline=1); l3 - | - | - | - sage: l3 + l5 - | - || - || - || - | - sage: l5 + l3 - | - || - || - || - | - sage: l5._baseline = 0 - sage: l5 + l3 - | - | - | - || - || - | - sage: l5._baseline = 4 - sage: l5 + l3 - | - || - || - | - | - | - sage: l3._baseline = 0 - sage: l3 + l5 - | - | - || - | - | - | - | - """ - new_matrix = [] - new_h = AsciiArt._compute_new_h(self, Nelt) - new_baseline = AsciiArt._compute_new_baseline(self, Nelt) - - if self._baseline is not None and Nelt._baseline is not None: - # left traitement - for line in self._matrix: - new_matrix.append(line + " "** Integer(self._l - len(line))) - - if new_h > self._h: - # | new_h > self._h - # | new_baseline > self._baseline - # ||<-- baseline number of white lines at the bottom - # | } :: Nelt._baseline - self._baseline - # | } - if new_baseline > self._baseline: - for k in range(new_baseline - self._baseline): - new_matrix.append(" " ** Integer(self._l)) - # | } new_h > self._h - # | } new_h - new_baseline > self._h - self._baseline - # ||<-- baseline number of white lines at the top - # || :: new_h - new_baseline - self._h + self._baseline - # || - # | - # | - if new_h - new_baseline > self._h - self._baseline: - for _ in range((new_h - new_baseline) - (self._h - self._baseline)): - new_matrix.insert(0, " " ** Integer(self._l)) - - # right traitement - i = 0 - if new_h > Nelt._h: - # | } new_h > Nelt._h - # | } new_h - new_baseline > Nelt._h - self._baseline - # ||<-- baseline number of white lines at the top - # || :: new_h - new_baseline - Nelt._h + Nelt._baseline - # || - # || - # | - - i = max(new_h - new_baseline - Nelt._h + Nelt._baseline , 0) - - for j in range(Nelt._h): - new_matrix[i+j] += Nelt._matrix[j] - else: - for line in self._matrix: - new_matrix.append(line + " " ** Integer(self._l - len(line))) - for i, line_i in enumerate(Nelt._matrix): - if i == len(new_matrix): - new_matrix.append(" "**Integer(self._l) + line_i) - else: new_matrix[i] += line_i - - # breakpoint - new_breakpoints = list(self._breakpoints) - new_breakpoints.append(self._l) - for bp in Nelt._breakpoints: - new_breakpoints.append(bp + self._l) - from sage.misc.misc import uniq - return AsciiArt( - lines = new_matrix, - breakpoints = uniq(new_breakpoints), - baseline = new_baseline, - atomic = False - ) - - def __mul__(self, Nelt): - r""" - The operator ``*`` is use to the representation ``self`` at - the top of an other ``Nelt``. - - TESTS:: - - sage: from sage.misc.ascii_art import AsciiArt - sage: cub = AsciiArt(lines=['***' for _ in range(3)]); cub - *** - *** - *** - sage: pyr = AsciiArt(lines=[' ^ ', '/ \\', '---']); pyr - ^ - / \ - --- - sage: cub * pyr - *** - *** - *** - ^ - / \ - --- - """ - new_repr = AsciiArt(self._matrix + Nelt._matrix) - return new_repr - -############## PRIMITIVES ################ - -def ascii_art(obj): - r""" - Return an ASCII art reprensentation of ``obj``:: - - sage: ascii_art(integral(exp(x+x^2)/(x+1), x)) - / - | - | 2 - | x + x - | e - | ------- dx - | x + 1 - | - / - - TESTS:: - - sage: n = var('n') - sage: ascii_art(sum(binomial(2 * n, n + 1) * x^n, n, 0, oo)) - / __________ \ - -\2*x + \/ -4*x + 1 - 1/ - -------------------------- - __________ - 2*x*\/ -4*x + 1 - sage: ascii_art(list(DyckWords(3))) - [ /\ ] - [ /\ /\ /\/\ / \ ] - [ /\/\/\, /\/ \, / \/\, / \, / \ ] - sage: ascii_art(1) - 1 - """ - if isinstance(obj, (tuple, list, dict, set)): - if obj.__class__ is tuple: - return ascii_art_tuple(obj) - elif obj.__class__ is dict: - return ascii_art_dict(obj) - elif obj.__class__ is list: - return ascii_art_list(obj) - else: - return ascii_art_set(obj) - if isinstance(obj, SageObject): - res = obj._ascii_art_() - else: res = AsciiArt(str(obj).splitlines()) - return res - -def _ascii_art_iter(iter, func=lambda elem, _: ascii_art(elem)): - r""" - Intermediate function for ``ascii_art_X`` where ``X`` is ``dict``, - ``set``, ``list``, or ``tuple``. - - TESTS:: - - sage: ascii_art(list(DyckWords(3))) - [ /\ ] - [ /\ /\ /\/\ / \ ] - [ /\/\/\, /\/ \, / \/\, / \, / \ ] - """ - repr_elems = AsciiArt([""]) - bool = False - for elem in iter: - if bool: - #print repr_elems.get_baseline() - if repr_elems._baseline is not None: - repr_elems += AsciiArt( - ["" ** Integer(repr_elems._h - 1 - repr_elems._baseline) ] + - [", "], - baseline=repr_elems._baseline) - else: - repr_elems += AsciiArt( - (["" ** Integer(repr_elems._h - 1) ] if repr_elems._h > 1 else [])+ - [", "], - baseline=repr_elems._baseline) - repr_elems._breakpoints.append(repr_elems._l - 1) - repr_elems += func(elem, iter) - bool = True - return repr_elems - -def ascii_art_set(set): - r""" - Return an ASCII art output of a set. - - TESTS:: - - sage: ascii_art(set(DyckWords(3))) - { /\ } - { /\ /\/\ /\ / \ } - { / \/\, / \, /\/\/\, /\/ \, / \ } - """ - repr_elems = _ascii_art_iter(set) - return _ascii_art_with_borders(repr_elems, "{ ", " }") - -def ascii_art_dict(dict): - r""" - Return an ASCII art output of a dictionnary. - - TESTS:: - - sage: ascii_art({i:dw for i,dw in enumerate(DyckWords(3))}) - { /\ } - { /\ /\ /\/\ / \ } - { 0:/\/\/\, 1:/\/ \, 2:/ \/\, 3:/ \, 4:/ \ } - """ - def func(k, dict): - return ascii_art(k) + AsciiArt([":"], baseline=0) + ascii_art(dict[k]) - repr_elems = _ascii_art_iter(dict, func) - return _ascii_art_with_borders(repr_elems, "{ ", " }") - -def _ascii_art_with_borders(repr_elems, l_border, r_border): - r""" - Intermediate function for elements with borders. - - TESTS:: - - sage: ascii_art(list(DyckWords(3))) - [ /\ ] - [ /\ /\ /\/\ / \ ] - [ /\/\/\, /\/ \, / \/\, / \, / \ ] - """ - new_mat = [] - for line in repr_elems: - new_mat.append(l_border + line + " "**Integer(len(repr_elems) - len(line)) + r_border) - new_bp = [bp + 2 for bp in repr_elems.get_breakpoints()] + [len(repr_elems) + 2] - return AsciiArt(new_mat, new_bp, baseline = 0, atomic = False) - -def ascii_art_list(list): - r""" - Return an ASCII art output of a list. - - TESTS:: - - sage: ascii_art(list(DyckWords(3))) - [ /\ ] - [ /\ /\ /\/\ / \ ] - [ /\/\/\, /\/ \, / \/\, / \, / \ ] - """ - repr_elems = _ascii_art_iter(list) - return _ascii_art_with_borders(repr_elems, "[ ", " ]") - -def ascii_art_tuple(tuple): - r""" - Return an ASCII art output of a tuple. - - TESTS:: - - sage: ascii_art(tuple(DyckWords(3))) - ( /\ ) - ( /\ /\ /\/\ / \ ) - ( /\/\/\, /\/ \, / \/\, / \, / \ ) - """ - repr_elems = _ascii_art_iter(tuple) - return _ascii_art_with_borders(repr_elems, "( ", " )") - -empty_ascii_art = AsciiArt([""]) +See :trac:`18357`. +""" +from sage.typeset.ascii_art import AsciiArt, ascii_art, empty_ascii_art diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index ee944e7f11e..858f87982f2 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -160,7 +160,12 @@ cdef class SageObject: except AttributeError: return str(type(self)) else: - return repr_func() + result = repr_func() + if isinstance(result, unicode): + # Py3 compatibility: allow _repr_ to return unicode + return result.encode('utf-8') + else: + return result def _ascii_art_(self): r""" @@ -174,12 +179,12 @@ cdef class SageObject: OUTPUT: - An :class:`~sage.misc.ascii_art.AsciiArt` object, see - :mod:`sage.misc.ascii_art` for details. + An :class:`~sage.typeset.ascii_art.AsciiArt` object, see + :mod:`sage.typeset.ascii_art` for details. EXAMPLES: - You can use the :func:`~sage.misc.ascii_art.ascii_art` function + You can use the :func:`~sage.typeset.ascii_art.ascii_art` function to get the ASCII art representation of any object in Sage:: sage: ascii_art(integral(exp(x+x^2)/(x+1), x)) @@ -220,11 +225,76 @@ cdef class SageObject: sage: 1._ascii_art_() 1 sage: type(_) - + """ - from sage.misc.ascii_art import AsciiArt + from sage.typeset.ascii_art import AsciiArt return AsciiArt(repr(self).splitlines()) + def _unicode_art_(self): + r""" + Return a unicode art representation. + + To implement multi-line unicode art output in a derived class + you must override this method. Unlike :meth:`_repr_`, which is + sometimes used for the hash key, the output of + :meth:`_unicode_art_` may depend on settings and is allowed to + change during runtime. + + OUTPUT: + + An :class:`~sage.typeset.unicode_art.UnicodeArt` object, see + :mod:`sage.typeset.unicode_art` for details. + + EXAMPLES: + + You can use the :func:`~sage.typeset.unicode_art.unicode_art` function + to get the ASCII art representation of any object in Sage:: + + sage: unicode_art(integral(exp(x+x^2)/(x+1), x)) + / + | + | 2 + | x + x + | e + | ------- dx + | x + 1 + | + / + + Alternatively, you can use the ``%display ascii_art/simple`` magic to + switch all output to ASCII art and back:: + + sage: from sage.repl.interpreter import get_test_shell + sage: shell = get_test_shell() + sage: shell.run_cell('tab = StandardTableaux(3)[2]; tab') + [[1, 2], [3]] + sage: shell.run_cell('%display ascii_art') + sage: shell.run_cell('tab') + 1 2 + 3 + sage: shell.run_cell('Tableaux.global_options(ascii_art="table", convention="French")') + sage: shell.run_cell('tab') + +---+ + | 3 | + +---+---+ + | 1 | 2 | + +---+---+ + sage: shell.run_cell('%display plain') + sage: shell.run_cell('Tableaux.global_options.reset()') + sage: shell.quit() + + TESTS:: + + sage: 1._ascii_art_() + 1 + sage: type(_) + + """ + ascii_art = self._ascii_art_() + lines = map(unicode, ascii_art) + from sage.typeset.unicode_art import UnicodeArt + return UnicodeArt(lines) + def __hash__(self): return hash(self.__repr__()) diff --git a/src/sage/typeset/__init__.py b/src/sage/typeset/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/typeset/all.py b/src/sage/typeset/all.py new file mode 100644 index 00000000000..f2327dfdb20 --- /dev/null +++ b/src/sage/typeset/all.py @@ -0,0 +1,4 @@ +from sage.misc.lazy_import import lazy_import + +lazy_import('sage.typeset.ascii_art', 'ascii_art') +lazy_import('sage.typeset.unicode_art', 'unicode_art') diff --git a/src/sage/typeset/ascii_art.py b/src/sage/typeset/ascii_art.py new file mode 100644 index 00000000000..01e3c464b63 --- /dev/null +++ b/src/sage/typeset/ascii_art.py @@ -0,0 +1,242 @@ +# -*- coding: utf-8 -*- +r""" +ASCII Art + +This file contains: + +- :class:`AsciiArt` an simple implementation of an ASCII art object, +- :func:`ascii_art` a function to get the ASCII art representation of any + object in Sage, + + +AUTHOR: + +- Jean-Baptiste Priez (2013-04): initial version + +EXAMPLES:: + + sage: n = var('n') + sage: integrate(n^2/x,x) + n^2*log(x) + sage: ascii_art(integrate(n^2/x,x)) + 2 + n *log(x) + sage: ascii_art(integrate(n^2/(pi*x),x)) + 2 + n *log(x) + --------- + pi + sage: ascii_art(list(Partitions(6))) + [ * ] + [ ** * ] + [ *** ** * * ] + [ **** *** * ** ** * * ] + [ ***** **** * *** ** * ** * * * ] + [ ******, * , ** , * , ***, * , * , **, * , * , * ] + +This method :meth:`ascii_art` could be automatically use by the display hook +manager activated by the magic function: ``%display ascii_art``:: + + sage: from sage.repl.interpreter import get_test_shell + sage: shell = get_test_shell() + sage: shell.run_cell('%display ascii_art') + sage: shell.run_cell("i = var('i')") + sage: shell.run_cell('sum(factorial(i)*x^i, i, 0, 10)') + 10 9 8 7 6 5 4 3 + 3628800*x + 362880*x + 40320*x + 5040*x + 720*x + 120*x + 24*x + 6*x + + 2 + + 2*x + x + 1 + sage: shell.run_cell('3/(7*x)') + 3 + --- + 7*x + sage: shell.run_cell('list(Compositions(5))') + [ * + [ * ** * * * + [ * * ** *** * ** * * ** * * + [ * * * * ** ** *** **** * * ** *** * ** * + [ *, * , * , * , * , * , * , * , **, ** , ** , ** , ***, *** , ****, + + ] + ] + ] + ] + ***** ] + sage: shell.run_cell('%display simple') + sage: shell.quit() + +:: + + . , , ... + .? .~?$NNO.II7. ..GOGG + ., ~7NNI7NDG$ ...~~~MGG + ..:IG.. ...G7:DDGNNDO.. ...7:~~~~GNN + .~~GGGGG... .O .=$+OD7GI$ ...GG:~+~~~~MG?M.7? + .~~~D~~GGGGDGOM$..~+IN=NDG.G::D~~~?~~~7?NDI:???G + .~~~~G:~~G~D:~NONGMMOGD$ND~~::I77~~$?++MN7G GI?D. + 7+I~~DG~~~N:GNGGOD$7OIOOMII::~:I7?G+OGN7G $III?. + .7==I~~~++IN?+++?$7ND$==$G:G??????N$OG$ ~III?7 + .$$+++?GG=+G?GDM?GOG:NGMGN7??????D$D~GODIII???. + D++++NG+DD$+=GGONGI$DNGDN??????,IN=DOGODN???. + , +~+++N??D$I+I=GNMDGDGNIINN??D+:::O~$GGGDIG?7 + :DD.:::OG?G=I~~G7GNM7777NIGODDNNG:::I$GGGGG7??O. + ~GDDM:::GGMG+?+~$NNN$7IG7NMNMMMN, =::I7GGGGGO???~ + :GDND7?::OOM.D+O~GGDI77777DMGNM$. ~:,$IGGGGGO???DO. + OGDDNN.D77OO. $?7==GG777$7GNGMGO. NOIGGGGGO???G$. + .OODNNN,DIGDM$GGGGG==GGGGIGNDMDMG, IGIGGGDON???GG: + .GODNDM.G$I$IOIOMG$G$?GGGIO,7OD7GG. ,GDIGGG??????GGG. + .DGDNI7I7MDI+OOODN$$O,$7DMIN,,IOO77O. G?$DIGGG?ID???OGG + GGDNNMMO7GD+OOOGIMOG7::NN====:?MMNGIDD,..IINIGGG?I??DIOGG? + .7ODMMMN.G7IOGOOODIMG,,:::$=~==::7OGG~IGOMDGMNIGGG????.OG$G. + ?ODDMNNNO,II$OGODMGDMM?:DMG==~MDINNM$.7$IONDGI?GGG????:$GGG. + .$MDMNNNNN..:?7GDDDGG,GGM?~:GGGDNND.GIM7D+GI$ON.:?GGG????$$OGG. + .7DNDDDNMDOGG.=IGGND=7II+N??::$GIIO,IIGMG?7I7G$ON?,IGGG????7GIGG. + ~GGGDNMMOOGGG$MGMMGDGMDGM?,G:GNG,:IIIGDG7IGGGGG$+NMIGGO?????IGGG. + .GGGDDMM7OGNGMODMNDDDOO.MII?GI$7IIIIING7GGDMM.IGDG.G7GGGG??$?7GGG + .IGDDDDOINNGMGMDMNDDGDO...$OI+??7OIIIDDGN+$==I=GD ?,NGGGGN7????GGG + 7GGNDM$GONDGDD$MMGNDN. G.:$$G$?$7II$GOO,O=+7O7O~N?OM?GGGGGD+??$GG + OOGDOI7DGMG..=~DG$DD. $,,$$$D??7ODOOOODG$777$G OGMM:GGGGGG??GGG. + .$GDDG?7DMOGDNNMGGDGG ..,=O7$GG$+O$$OG=+O:GI77G$. ...DIGGGGI?$GGG. + .OGGGGO7OGDGGNGGGDGG. O:,77$O$$$D $GN:7777GD ..$GGGG??GGG$. + .GGGGDI GD.~NOGDGGG MG,777G77D7 +$~D77777= ..7GGG???GGO. + GGD. .~NODN... ..D::77O$7G77 .OG:G???G7. 7$NOM??GG=. + . . . .+~~DD~77$G7OD7 .?GGO?$OM$D ?????DGG... + 7I77DN$II7$M.G$G . .:G?==$G .?????D??~ + . .:IIIGO7O$GN..O$ 7I=~+?I. =???7? GGIG + $,III7NGGNNNG, .O. O====7I. .DG??$?. GG?G + .$7III$77777$$~ . +====D$GG ~$???: .$GIG. + :.+IIII$77$G$$O,+ OI==+7I$7=.:??? .GG$. + ,,III$I7?I7$$OG7D ?+,==+77G?$,???. GG? + ?IIIII=+$I77777 .:++=+++O77:,??: GG. + .IIIIG+III77777. 7,=,++?II7$,?G .GO . + $OII7?OIIIIIO .I:++??IIIII???. I$ . + .DNGDMOIG777. +~++?I$IIMN?, .G, + .G.I?IIDGII~ .OG??$$MIMGI. G, + N+?II7OGI7. DD?$GGDGID. : + =?IIOOI$. ?~~GGG$II .+. + ,7=IINGI ?=IGGGI?. + G,+IGOII ?+II7??+ + .,:?IGOII ?+GIOII + .:N+=?IMGI7=. .~:$I?IO. + .$:IGO?$IIIII7+. O::I7I?. + .:::=IIGIIIIIII. .~,$IG?IO + .+:$IIIIMIII7G$?. I::I7IIG7 + .$I$+7IIIMMMG7I7 ?G,DGGNII. + .$~:$IIGMNGND77I: +I$GOD=?= + ?=?IIIIGIIIINI7777? .7~=~===?. + ONGNDG??IG?III$N7I777 D~====I + .:$??7IIIIIII.....,.... O::==~$D. + .,........ .. ..M:I.==$7G. + I?::IIIII7. + .~:G:IIIIIG. + .$:,O7III$$O + ::~DOGGNNO + .::,IOODI, + .7????$. + ... . +""" +#******************************************************************************* +# Copyright (C) 2013 Jean-Baptiste Priez , +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# This code is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# The full text of the GPL is available at: +# +# http://www.gnu.org/licenses/ +#******************************************************************************* + +from sage.typeset.character_art import CharacterArt +from sage.typeset.character_art_factory import CharacterArtFactory +import sage.typeset.symbols as symbol + + +class AsciiArt(CharacterArt): + r""" + An Ascii art object is an object with some specific representation for + *printing*. + + INPUT: + + - ``lines`` -- the list of lines of the representation of the ascii art + object + + - ``breakpoints`` -- the list of points where the representation can be + split + + - ``baseline`` -- the reference line (from the bottom) + + - ``atomic`` -- indicate if the ascii art representation is splittable + (must be coherent with breakpoints) + + EXAMPLES:: + + sage: i = var('i') + sage: ascii_art(sum(pi^i/factorial(i)*x^i, i, 0, oo)) + pi*x + e + """ + _string_type = str + + + +_ascii_art_factory = CharacterArtFactory( + AsciiArt, str, '_ascii_art_', + (symbol.ascii_left_parenthesis, symbol.ascii_right_parenthesis), + (symbol.ascii_left_square_bracket, symbol.ascii_right_square_bracket), + (symbol.ascii_left_curly_brace, symbol.ascii_right_curly_brace), +) + + +def ascii_art(obj): + r""" + Return an ASCII art representation + + INPUT: + + - ``obj`` -- anything. The object whose ascii art representation + we want. + + OUTPUT: + + :class:`AsciiArt` instance. + + EXAMPLES:: + + sage: ascii_art(integral(exp(x+x^2)/(x+1), x)) + / + | + | 2 + | x + x + | e + | ------- dx + | x + 1 + | + / + + TESTS:: + + sage: n = var('n') + sage: ascii_art(sum(binomial(2 * n, n + 1) * x^n, n, 0, oo)) + / __________ \ + -\2*x + \/ -4*x + 1 - 1/ + -------------------------- + __________ + 2*x*\/ -4*x + 1 + sage: ascii_art(list(DyckWords(3))) + [ /\ ] + [ /\ /\ /\/\ / \ ] + [ /\/\/\, /\/ \, / \/\, / \, / \ ] + sage: ascii_art(1) + 1 + """ + return _ascii_art_factory.build(obj) + + +empty_ascii_art = _ascii_art_factory.build_empty() + diff --git a/src/sage/typeset/character_art.py b/src/sage/typeset/character_art.py new file mode 100644 index 00000000000..101d303d6a9 --- /dev/null +++ b/src/sage/typeset/character_art.py @@ -0,0 +1,722 @@ +# -*- coding: utf-8 -*- +r""" +Base Class for Character-Based Art + +This is the common base class for +:class:`sage.typeset.ascii_art.AsciiArt` and +:class:`sage.typeset.ascii_art.UnicodeArt`. They implement simple +graphics by placing characters on a rectangular grid, in other words, +using monospace fonts. The difference is that one is restricted to +7-bit ascii, the other uses all unicode code points. + """ + +#******************************************************************************* +# Copyright (C) 2013 Jean-Baptiste Priez , +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# This code is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# The full text of the GPL is available at: +# +# http://www.gnu.org/licenses/ +#******************************************************************************* + +import os, sys +from sage.structure.sage_object import SageObject +from sage.rings.integer import Integer + + +################################################################################ +### Global variable use to compute the maximal length allows for ascii art +### object. +MAX_WIDTH = None +################################################################################ + +class CharacterArt(SageObject): + + def __init__(self, lines=[], breakpoints=[], baseline=None, atomic=True): + r""" + Abstract base class for character art + + INPUT: + + - ``lines`` -- the list of lines of the representation of the + character art object + + - ``breakpoints`` -- the list of points where the representation can be + split + + - ``baseline`` -- the reference line (from the bottom) + + - ``atomic`` -- indicate if the character art representation is + splittable (must be coherent with breakpoints) + + EXAMPLES:: + + sage: i = var('i') + sage: ascii_art(sum(pi^i/factorial(i)*x^i, i, 0, oo)) + pi*x + e + + TESTS:: + + sage: from sage.typeset.ascii_art import AsciiArt + sage: aao = AsciiArt() + sage: aao.is_atomic() + True + sage: aao + + sage: aa = AsciiArt([" * ", " * * ", "*****"]); aa + * + * * + ***** + """ + self._is_uniq = True + self._matrix = lines + self._breakpoints = breakpoints + self._baseline = baseline if baseline is not None else 0 + self._is_atomic = atomic + + self._h = len(lines) + self._l = max(map(lambda line: len(line), lines) + [0]) + + @classmethod + def empty(cls): + """ + Return the empty character art object + + EXAMPLES:: + + sage: from sage.typeset.ascii_art import AsciiArt + sage: AsciiArt.empty() + """ + empty_string = cls._string_type() + return cls([empty_string]) + + def __getitem__(self, key): + r""" + Return the line `key` of the ASCII art object. + + TESTS:: + + sage: from sage.typeset.ascii_art import AsciiArt + sage: p5 = AsciiArt([" * ", " * * ", "*****"]) + sage: p5[1] + ' * * ' + """ + return self._matrix[key] + + def __iter__(self): + r""" + Iterator on all lines of the ASCII art object. + + TESTS:: + + sage: from sage.typeset.ascii_art import AsciiArt + sage: p5 = AsciiArt([" * ", " * * ", "*****"]) + sage: for line in p5: + ....: print line + * + * * + ***** + """ + for elem in self._matrix: + yield elem + + def _repr_(self): + r""" + TESTS:: + + sage: from sage.typeset.ascii_art import AsciiArt + sage: p5 = AsciiArt([" * ", " * * ", "*****"]) + sage: repr(p5) + ' * \n * * \n*****' + """ + # Compute the max length of a draw + global MAX_WIDTH + if MAX_WIDTH is not None: + hsize = MAX_WIDTH + else: + hsize = self._terminal_width() + ######### + # if the draw is larger than the max length it try to split... + if hsize <= self._l and len(self._breakpoints) > 0: + return self._split_repr_(hsize) + ######### + output = "" + if len(self._matrix) > 0: + for i in range(len(self._matrix) - 1): + output += self._matrix[i] + "\n" + return output + self._matrix[len(self._matrix) - 1] + return output + + def is_atomic(self): + r""" + Return ``True`` if the object is not splitable + + For example, we considere a linear expression:: + + sage: a = 14*x^5 + 5*x^4 + sage: ascii_art(a) + 5 4 + 14*x + 5*x + + If ASCII art object is not atomic, it is splittable on the ``+`` + (in fact it is not really true because we use ``sympy`` to make + ASCII art). + + TESTS:: + + sage: from sage.typeset.ascii_art import AsciiArt + sage: aa = AsciiArt([" * ", " * * ", "*****"]); aa + * + * * + ***** + sage: aa.is_atomic() + True + sage: laa = ascii_art([aa,aa]) + sage: laa.is_atomic() + False + """ + return self._is_atomic + + def get_baseline(self): + r""" + Return the line where the baseline is, for example:: + + 5 4 + 14*x + 5*x + + the baseline has at line `0` and :: + + { o } + { \ : 4 } + { o } + + has at line `1`. + + TESTS:: + + sage: from sage.typeset.ascii_art import AsciiArt + sage: aa = AsciiArt([" * ", " * * ", " * * ", "*******"], baseline=1);aa + * + * * + * * + ******* + sage: aa.get_baseline() + 1 + sage: b = AsciiArt(["<-"]) + sage: aa+b + * + * * + * * <- + ******* + """ + return self._baseline + + def get_breakpoints(self): + r""" + Return an iterator of breakpoints where the object can be split. + + For example the expression:: + + 5 4 + 14x + 5x + + can be split on position 4 (on the ``+``). + + EXAMPLES:: + + sage: from sage.typeset.ascii_art import AsciiArt + sage: p3 = AsciiArt([" * ", "***"]) + sage: p5 = AsciiArt([" * ", " * * ", "*****"]) + sage: aa = ascii_art([p3, p5]) + sage: aa.get_breakpoints() + [2, 5, 6, 7, 12] + """ + return self._breakpoints + + def _isatty(self): + """ + Test whether stdout is a TTY + + If this test succeeds, you can assume that stdout is directly + connected to a terminal. Otherwise you should treat stdout as + being redirected to a file. + + OUTPUT: + + Boolean + + EXAMPLES:: + + sage: from sage.typeset.ascii_art import empty_ascii_art + sage: empty_ascii_art._isatty() + False + """ + from sage.doctest import DOCTEST_MODE + if DOCTEST_MODE: + return False + try: + return os.isatty(sys.stdout.fileno()) + except Exception: + # The IPython zeromq kernel uses a fake stdout that does + # not support fileno() + return False + + def _terminal_width(self): + """ + Compute the width size of the terminal. + + EXAMPLES:: + + sage: from sage.typeset.ascii_art import empty_ascii_art + sage: empty_ascii_art._terminal_width() + 80 + """ + if not self._isatty(): + return 80 + import fcntl, termios, struct + rc = fcntl.ioctl(int(0), termios.TIOCGWINSZ, + struct.pack('HHHH', sys.stdout.fileno(), 0, 0, 0)) + h, w, hp, wp = struct.unpack('HHHH', rc) + return w + + def _split_repr_(self, size): + r""" + Split the draw and the left part has length ``size``. + + TESTS:: + + sage: from sage.typeset.ascii_art import AsciiArt + sage: p3 = AsciiArt([" * ", "***"]) + sage: p5 = AsciiArt([" * ", " * * ", "*****"]) + sage: aa = ascii_art([p3, p5]) + sage: print aa._split_repr_(6) + [ + [ * + [ *** + + * ] + * * ] + , ***** ] + """ + import sys + f_split = self._breakpoints[0]; i = 1 + while i < len(self._breakpoints) and self._breakpoints[i] < size: + f_split = self._breakpoints[i] + i += 1 + if size <= f_split: + import warnings + warnings.warn("the console size is smaller than the pretty" + + "representation of the object") + top, bottom = self.split(f_split) + return (top * self.empty()).__repr__() + "\n" + bottom.__repr__() + + def split(self, pos): + r""" + Split the representation at the position ``pos``. + + EXAMPLES:: + + sage: from sage.typeset.ascii_art import AsciiArt + sage: p3 = AsciiArt([" * ", "***"]) + sage: p5 = AsciiArt([" * ", " * * ", "*****"]) + sage: aa = ascii_art([p3, p5]) + sage: a,b= aa.split(6) + sage: a + [ + [ * + [ ***, + sage: b + * ] + * * ] + ***** ] + """ + left = []; right = [] + for line in self: + left.append(line[:pos]) + right.append(line[pos:]) + l_bp = []; r_bp = [] + for bp in self._breakpoints: + if bp < pos: + l_bp.append(bp) + elif bp > pos: + r_bp.append(bp - pos) + return self.__class__(left, l_bp), self.__class__(right, r_bp) + + @staticmethod + def _compute_new_baseline(obj1, obj2): + r""" + TESTS:: + + sage: from sage.typeset.ascii_art import AsciiArt + sage: l5 = AsciiArt(lines = ['|' for _ in range(5)], baseline = 2); l5 + | + | + | + | + | + sage: l3 = AsciiArt(lines = ['|' for _ in range(3)], baseline = 1); l3 + | + | + | + sage: AsciiArt._compute_new_baseline(l5, l3) + 2 + sage: l5 + l3 + | + || + || + || + | + sage: l5._baseline = 0 + sage: AsciiArt._compute_new_baseline(l5, l3) + 1 + sage: l5 + l3 + | + | + | + || + || + | + sage: l5._baseline = 4 + sage: AsciiArt._compute_new_baseline(l5, l3) + 4 + sage: l5 + l3 + | + || + || + | + | + | + sage: l3._baseline = 0 + sage: AsciiArt._compute_new_baseline(l3, l5) + 4 + sage: l3 + l5 + | + | + || + | + | + | + | + sage: l5._baseline = None + sage: AsciiArt._compute_new_baseline(l3, l5) + 2 + sage: l3._baseline = 2 + sage: AsciiArt._compute_new_baseline(l3, l5) + 4 + sage: l3 + l5 + || + || + || + | + | + + """ + if obj1.get_baseline() is None: + if obj2.get_baseline() is None: + return None + return obj2.get_baseline() + max(obj1._h - obj2._h, 0) + if obj2.get_baseline() is None: + return obj1.get_baseline() + max(obj2._h - obj1._h, 0) + return max( + obj1.get_baseline(), + obj2.get_baseline() + ) + + @staticmethod + def _compute_new_h(obj1, obj2): + r""" + TESTS:: + + sage: from sage.typeset.ascii_art import AsciiArt + sage: l5 = AsciiArt(lines=['|' for _ in range(5)], baseline=2); l5 + | + | + | + | + | + sage: l3 = AsciiArt(lines=['|' for _ in range(3)], baseline=1); l3 + | + | + | + sage: AsciiArt._compute_new_h(l5, l3) + 5 + sage: l5 + l3 + | + || + || + || + | + sage: l5._baseline = 0 + sage: AsciiArt._compute_new_h(l5, l3) + 6 + sage: l5 + l3 + | + | + | + || + || + | + sage: l5._baseline = 4 + sage: AsciiArt._compute_new_h(l5, l3) + 6 + sage: l5 + l3 + | + || + || + | + | + | + sage: l3._baseline = 0 + sage: AsciiArt._compute_new_h(l3, l5) + 7 + sage: l3 + l5 + | + | + || + | + | + | + | + """ + if obj1.get_baseline() is None or obj2.get_baseline() is None: + return max(obj1._h, obj2._h) + return max( + obj1.get_baseline(), + obj2.get_baseline() + ) + max( + obj1._h - obj1.get_baseline(), + obj2._h - obj2.get_baseline() + ) + + def width(self): + r""" + Return the length (width) of the ASCII art object. + + OUTPUT: + + Integer. The number of characters in each line. + + TESTS:: + + sage: from sage.typeset.ascii_art import AsciiArt + sage: p3 = AsciiArt([" * ", "***"]) + sage: len(p3), p3.width(), p3.height() + (3, 3, 2) + sage: p5 = AsciiArt([" * ", " * * ", "*****"]) + sage: len(p5), p5.width(), p5.height() + (5, 5, 3) + """ + return self._l + + __len__ = width + + def height(self): + r""" + Return the height of the ASCII art object. + + OUTPUT: + + Integer. The number of lines. + + TESTS:: + + sage: from sage.typeset.ascii_art import AsciiArt + sage: p3 = AsciiArt([" * ", "***"]) + sage: p3.height() + 2 + sage: p5 = AsciiArt([" * ", " * * ", "*****"]) + sage: p5.height() + 3 + """ + return self._h + + def __add__(self, Nelt): + r""" + Concatenate two ascii art object. + + By default, when two object are concatenated, the new one will be + splittable between both. + + If the baseline is defined, the concatenation is computed such that the + new baseline coincidate with the olders. + + For example, let `T` be a tree with it's baseline ascii art + representation in the middle:: + + o + \ + o + / \ + o o + + and let `M` be a matrix with it's baseline ascii art representation at + the middle two:: + + [1 2 3] + [4 5 6] + [7 8 9] + + then the concatenation of both will give:: + + o + \ [1 2 3] + o [4 5 6] + / \ [7 8 9] + o o + + If one of the objects has not baseline, the concatenation is realized + from the top:: + + o [1 2 3] + \ [4 5 6] + o [7 8 9] + / \ + o o + + TESTS:: + + sage: from sage.typeset.ascii_art import AsciiArt + sage: l5 = AsciiArt(lines=['|' for _ in range(5)], baseline=2); l5 + | + | + | + | + | + sage: l3 = AsciiArt(lines=['|' for _ in range(3)], baseline=1); l3 + | + | + | + sage: l3 + l5 + | + || + || + || + | + sage: l5 + l3 + | + || + || + || + | + sage: l5._baseline = 0 + sage: l5 + l3 + | + | + | + || + || + | + sage: l5._baseline = 4 + sage: l5 + l3 + | + || + || + | + | + | + sage: l3._baseline = 0 + sage: l3 + l5 + | + | + || + | + | + | + | + """ + new_matrix = [] + new_h = self.__class__._compute_new_h(self, Nelt) + new_baseline = self.__class__._compute_new_baseline(self, Nelt) + + if self._baseline is not None and Nelt._baseline is not None: + # left treatement + for line in self._matrix: + new_matrix.append(line + " "** Integer(self._l - len(line))) + + if new_h > self._h: + # | new_h > self._h + # | new_baseline > self._baseline + # ||<-- baseline number of white lines at the bottom + # | } :: Nelt._baseline - self._baseline + # | } + if new_baseline > self._baseline: + for k in range(new_baseline - self._baseline): + new_matrix.append(" " ** Integer(self._l)) + # | } new_h > self._h + # | } new_h - new_baseline > self._h - self._baseline + # ||<-- baseline number of white lines at the top + # || :: new_h - new_baseline - self._h + self._baseline + # || + # | + # | + if new_h - new_baseline > self._h - self._baseline: + for _ in range((new_h - new_baseline) - (self._h - self._baseline)): + new_matrix.insert(0, " " ** Integer(self._l)) + + # right treatement + i = 0 + if new_h > Nelt._h: + # | } new_h > Nelt._h + # | } new_h - new_baseline > Nelt._h - self._baseline + # ||<-- baseline number of white lines at the top + # || :: new_h - new_baseline - Nelt._h + Nelt._baseline + # || + # || + # | + i = max(new_h - new_baseline - Nelt._h + Nelt._baseline , 0) + for j in range(Nelt._h): + new_matrix[i+j] += Nelt._matrix[j] + else: + for line in self._matrix: + new_matrix.append(line + " " ** Integer(self._l - len(line))) + for i, line_i in enumerate(Nelt._matrix): + if i == len(new_matrix): + new_matrix.append(" "**Integer(self._l) + line_i) + else: new_matrix[i] += line_i + + # breakpoint + new_breakpoints = list(self._breakpoints) + new_breakpoints.append(self._l) + for bp in Nelt._breakpoints: + new_breakpoints.append(bp + self._l) + from sage.misc.misc import uniq + return self.__class__( + lines=new_matrix, + breakpoints=uniq(new_breakpoints), + baseline=new_baseline, + atomic=False + ) + + def __mul__(self, Nelt): + r""" + The operator ``*`` is use to the representation ``self`` at + the top of an other ``Nelt``. + + TESTS:: + + sage: from sage.typeset.ascii_art import AsciiArt + sage: cub = AsciiArt(lines=['***' for _ in range(3)]); cub + *** + *** + *** + sage: pyr = AsciiArt(lines=[' ^ ', '/ \\', '---']); pyr + ^ + / \ + --- + sage: cub * pyr + *** + *** + *** + ^ + / \ + --- + """ + new_repr = self.__class__(self._matrix + Nelt._matrix) + return new_repr diff --git a/src/sage/typeset/character_art_factory.py b/src/sage/typeset/character_art_factory.py new file mode 100644 index 00000000000..37e74edf328 --- /dev/null +++ b/src/sage/typeset/character_art_factory.py @@ -0,0 +1,324 @@ +# -*- coding: utf-8 -*- +r""" +Factory for Character-Based Art +""" +#******************************************************************************* +# Copyright (C) 2013 Jean-Baptiste Priez , +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# This code is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# The full text of the GPL is available at: +# +# http://www.gnu.org/licenses/ +#******************************************************************************* + +from sage.structure.sage_object import SageObject +from sage.rings.all import ZZ + + +class CharacterArtFactory(SageObject): + + def __init__(self, + art_type, string_type, magic_method_name, + parenthesis, square_bracet, curly_brace): + """ + Abstract base class for character art factory + + This class is the common implementation behind + :func:`~sage.typeset.ascii_art.ascii_art` and + :func:`sage.typeset.unicode_art.unicode_art`. + + INPUT: + + - ``art_type` -- type of the character art. + + - ``string_type`` -- type of strings (the lines in the + character art). + + - ``magic_method_name`` -- name of the Sage magic method. + + - ``parenthesis`` -- left/right pair of two multi-line + symbols. The parenthesis, a.k.a. round brackets. + + - ``square_bracket`` -- left/right pair of two multi-line + symbols. The square_brackets. + + - ``curly_brace`` -- left/right pair of two multi-line + symbols. The curly braces. + + EXAMPLES:: + + sage: from sage.typeset.ascii_art import _ascii_art_factory as factory + sage: type(factory) + + """ + self.art_type = art_type + assert string_type in [str, unicode] + self.string_type = string_type + assert magic_method_name in ['_ascii_art_', '_unicode_art_'] + self.magic_method_name = magic_method_name + self.left_parenthesis, self.right_parenthesis = parenthesis + self.left_square_bracket, self.right_square_bracket = square_bracet + self.left_curly_brace, self.right_curly_brace = curly_brace + + def build(self, obj): + r""" + Construct a character art reprensentation + + INPUT: + + - ``obj`` -- anything. The object whose ascii art representation + we want. + + OUTPUT: + + Character art object. + + EXAMPLES:: + + sage: ascii_art(integral(exp(x+x^2)/(x+1), x)) + / + | + | 2 + | x + x + | e + | ------- dx + | x + 1 + | + / + + TESTS:: + + sage: n = var('n') + sage: ascii_art(sum(binomial(2 * n, n + 1) * x^n, n, 0, oo)) + / __________ \ + -\2*x + \/ -4*x + 1 - 1/ + -------------------------- + __________ + 2*x*\/ -4*x + 1 + sage: ascii_art(list(DyckWords(3))) + [ /\ ] + [ /\ /\ /\/\ / \ ] + [ /\/\/\, /\/ \, / \/\, / \, / \ ] + sage: ascii_art(1) + 1 + """ + if isinstance(obj, self.art_type): + return obj + elif isinstance(obj, (tuple, list, dict, set)): + if obj.__class__ is tuple: + return self.build_tuple(obj) + elif obj.__class__ is dict: + return self.build_dict(obj) + elif obj.__class__ is list: + return self.build_list(obj) + else: + return self.build_set(obj) + elif isinstance(obj, SageObject): + return self.build_from_magic_method(obj) + else: + return self.build_from_string(obj) + + def build_empty(self): + """ + Return the empty character art object + + OUTPUT: + + Character art instance. + """ + return self.art_type.empty() + + def build_from_magic_method(self, obj): + """ + Return the character art object created by the object's magic method + + OUTPUT: + + Character art instance. + + EXAMPLES:: + + sage: from sage.typeset.ascii_art import _ascii_art_factory as factory + sage: out = factory.build_from_magic_method(identity_matrix(2)); out + [1 0] + [0 1] + sage: type(out) + + """ + magic_method = getattr(obj, self.magic_method_name) + return magic_method() + + def build_from_string(self, obj): + """ + Return the character art object created from splitting the object's string representation + + OUTPUT: + + Character art instance. + + EXAMPLES:: + + sage: from sage.typeset.ascii_art import _ascii_art_factory as factory + sage: out = factory.build_from_string('a\nbb\nccc') + sage: out + out + out + a a a + bb bb bb + ccccccccc + sage: type(out) + + """ + lines = self.string_type(obj).splitlines() + return self.art_type(lines) + + def build_comma_sequence(self, iterable, func=None): + r""" + Build up a comma-separated sequence + + Auxiliary function for ``build_X`` where ``X`` is ``dict``, + ``set``, ``list``, or ``tuple``. + + EXAMPLES:: + + sage: from sage.typeset.ascii_art import _ascii_art_factory as factory + sage: out = factory.build_comma_sequence(list(DyckWords(3))); out + /\ + /\ /\ /\/\ / \ + /\/\/\, /\/ \, / \/\, / \, / \ + sage: type(out) + + """ + if func is None: + func = lambda elem, _: self.build(elem) + repr_elems = self.build_empty() + not_first = False + for elem in iterable: + if not_first: + #print repr_elems.get_baseline() + if repr_elems._baseline is not None: + repr_elems += self.art_type( + ["" ** ZZ(repr_elems._h - 1 - repr_elems._baseline) ] + + [", "], + baseline=repr_elems._baseline) + else: + repr_elems += self.art_type( + (["" ** ZZ(repr_elems._h - 1) ] if repr_elems._h > 1 else [])+ + [", "], + baseline=repr_elems._baseline) + repr_elems._breakpoints.append(repr_elems._l - 1) + repr_elems += func(elem, iterable) + not_first = True + return repr_elems + + def build_set(self, set): + r""" + Return an character art output of a set. + + TESTS:: + + sage: ascii_art(set(DyckWords(3))) + { /\ } + { /\ /\/\ /\ / \ } + { / \/\, / \, /\/\/\, /\/ \, / \ } + """ + repr_elems = self.build_comma_sequence(set) + return self.build_container( + repr_elems, self.left_curly_brace, self.right_curly_brace) + + def build_dict(self, dict): + r""" + Return an character art output of a dictionnary. + + TESTS:: + + sage: ascii_art({i:dw for i,dw in enumerate(DyckWords(3))}) + { /\ } + { /\ /\ /\/\ / \ } + { 0:/\/\/\, 1:/\/ \, 2:/ \/\, 3:/ \, 4:/ \ } + """ + colon = self.art_type([self.string_type(':')], baseline=0) + def func(k, dict): + return self.build(k) + colon + self.build(dict[k]) + repr_elems = self.build_comma_sequence(dict, func) + return self.build_container( + repr_elems, self.left_curly_brace, self.right_curly_brace) + + def build_container(self, content, left_border, right_border): + r""" + Return character art for a container + + INPUT: + + - ``content`` -- + :class:`~sage.typeset.character_art.CharacterArt`. The + content of the container, usually comma-separated entries. + + - ``left_border`` -- + :class:`~sage.typeset.symbols.CompoundSymbol`. The left + border of the container. + + - ``right_border`` -- + :class:`~sage.typeset.symbols.CompoundSymbol`. The right + border of the container. + + TESTS:: + + sage: l = ascii_art(list(DyckWords(3))); l + [ /\ ] + [ /\ /\ /\/\ / \ ] + [ /\/\/\, /\/ \, / \/\, / \, / \ ] + sage: l.get_breakpoints() + [2, 8, 9, 10, 16, 17, 18, 24, 25, 26, 32, 33, 34, 40] + """ + # new_mat = [] + # for line in repr_elems: + # new_mat.append(l_border + line + " "**ZZ(len(repr_elems) - len(line)) + r_border) + w = content.width() + h = content.height() + left_border = left_border.character_art(h) + right_border = right_border.character_art(h) + lines = [] + pad = self.string_type(' ') + for left, line, right in zip(left_border, content, right_border): + lines.append(left + pad + line.ljust(w) + pad + right) + shift = len(left_border) + len(pad) + basepoints = [bp + shift for bp in content.get_breakpoints()] + [w+shift] + return self.art_type(lines, basepoints, baseline=0, atomic=False) + + def build_list(self, list): + r""" + Return an character art output of a list. + + TESTS:: + + sage: l = ascii_art(list(DyckWords(3))); l + [ /\ ] + [ /\ /\ /\/\ / \ ] + [ /\/\/\, /\/ \, / \/\, / \, / \ ] + sage: l.get_breakpoints() + [2, 8, 9, 10, 16, 17, 18, 24, 25, 26, 32, 33, 34, 40] + """ + repr_elems = self.build_comma_sequence(list) + return self.build_container( + repr_elems, self.left_square_bracket, self.right_square_bracket) + + def build_tuple(self, tuple): + r""" + Return an character art output of a tuple. + + TESTS:: + + sage: ascii_art(tuple(DyckWords(3))) + ( /\ ) + ( /\ /\ /\/\ / \ ) + ( /\/\/\, /\/ \, / \/\, / \, / \ ) + """ + repr_elems = self.build_comma_sequence(tuple) + return self.build_container( + repr_elems, self.left_parenthesis, self.right_parenthesis) + diff --git a/src/sage/typeset/symbols.py b/src/sage/typeset/symbols.py new file mode 100644 index 00000000000..9ec5a540cea --- /dev/null +++ b/src/sage/typeset/symbols.py @@ -0,0 +1,340 @@ +# -*- coding: utf-8 -*- +""" +Symbols for Character Art + +This module defines single- and multi-line symbols. + +EXAMPLES:: + + sage: from sage.typeset.symbols import * + + sage: symbols = ascii_art(u'') + sage: for i in range(1, 5): + ....: symbols += ascii_left_parenthesis.character_art(i) + ....: symbols += ascii_art(u' ') + ....: symbols += ascii_right_parenthesis.character_art(i) + ....: symbols += ascii_art(u' ') + sage: for i in range(1, 5): + ....: symbols += ascii_left_square_bracket.character_art(i) + ....: symbols += ascii_art(u' ') + ....: symbols += ascii_right_square_bracket.character_art(i) + ....: symbols += ascii_art(u' ') + sage: for i in range(1, 5): + ....: symbols += ascii_left_curly_brace.character_art(i) + ....: symbols += ascii_art(u' ') + ....: symbols += ascii_right_curly_brace.character_art(i) + ....: symbols += ascii_art(u' ') + sage: symbols + ( ) [ ] { } + ( ) ( ) [ ] [ ] { } { } + ( ) ( ) ( ) [ ] [ ] [ ] { } { } { } + ( ) ( ) ( ) ( ) [ ] [ ] [ ] [ ] { } { } { } { } + + sage: symbols = unicode_art(u'') + sage: for i in range(1, 5): + ....: symbols += unicode_left_parenthesis.character_art(i) + ....: symbols += unicode_art(u' ') + ....: symbols += unicode_right_parenthesis.character_art(i) + ....: symbols += unicode_art(u' ') + sage: for i in range(1, 5): + ....: symbols += unicode_left_square_bracket.character_art(i) + ....: symbols += unicode_art(u' ') + ....: symbols += unicode_right_square_bracket.character_art(i) + ....: symbols += unicode_art(u' ') + sage: for i in range(1, 5): + ....: symbols += unicode_left_curly_brace.character_art(i) + ....: symbols += unicode_art(u' ') + ....: symbols += unicode_right_curly_brace.character_art(i) + ....: symbols += unicode_art(u' ') + sage: symbols + ⎛ ⎞ ⎡ ⎤ ⎧ ⎫ + ⎛ ⎞ ⎜ ⎟ ⎡ ⎤ ⎢ ⎥ ⎧ ⎫ ⎭ ⎩ + ⎛ ⎞ ⎜ ⎟ ⎜ ⎟ ⎡ ⎤ ⎢ ⎥ ⎢ ⎥ ⎰ ⎱ ⎨ ⎬ ⎫ ⎧ + ( ) ⎝ ⎠ ⎝ ⎠ ⎝ ⎠ [ ] ⎣ ⎦ ⎣ ⎦ ⎣ ⎦ { } ⎱ ⎰ ⎩ ⎭ ⎩ ⎭ +""" + +import unicodedata +from sage.structure.sage_object import SageObject + + +class CompoundSymbol(SageObject): + + def __init__(self, character, top, extension, bottom, + middle=None, + middle_top=None, middle_bottom=None, + top_2=None, bottom_2=None): + """ + A multi-character (ascii/unicode art) symbol + + INPUT: + + Instead of string, each of these can be unicode in Python 2: + + - ``character`` -- string. The single-line version of the symbol. + + - ``top`` -- string. The top line of a multi-line symbol. + + - ``extension`` -- string. The extension line of a multi-line symbol (will + be repeated). + + - ``bottom`` -- string. The bottom line of a multi-line symbol. + + - ``middle`` -- optional string. The middle part, for example + in curly braces. Will be used only once for the symbol, and + only if its height is odd. + + - ``middle_top`` -- optional string. The upper half of the + 2-line middle part if the height of the symbol is even. + Will be used only once for the symbol. + + - ``middle_bottom`` -- optional string. The lower half of the + 2-line middle part if the height of the symbol is even. + Will be used only once for the symbol. + + - ``top_2`` -- optional string. The upper half of a 2-line symbol. + + - ``bottom_2`` -- optional string. The lower half of a 2-line symbol. + + EXAMPLES:: + + sage: from sage.typeset.symbols import CompoundSymbol + sage: i = CompoundSymbol('I', '+', '|', '+', '|') + sage: i.print_to_stdout(1) + I + sage: i.print_to_stdout(3) + + + | + + + """ + self.character = character + self.top = top + self.extension = extension + self.bottom = bottom + self.middle = middle or extension + self.middle_top = middle_top or extension + self.middle_bottom = middle_bottom or extension + self.top_2 = top_2 or top + self.bottom_2 = bottom_2 or bottom + + def _repr_(self): + """ + Return string representation + + EXAMPLES:: + + sage: from sage.typeset.symbols import unicode_left_parenthesis + sage: unicode_left_parenthesis + multi_line version of "(" + """ + return u'multi_line version of "{0}"'.format(self.character).encode('utf-8') + + def __call__(self, num_lines): + r""" + Return the lines for a multi-line symbol + + INPUT: + + - ``num_lines`` -- integer. The total number of lines. + + OUTPUT: + + List of strings / unicode strings. + + EXAMPLES:: + + sage: from sage.typeset.symbols import unicode_left_parenthesis + sage: unicode_left_parenthesis(4) + [u'\u239b', u'\u239c', u'\u239c', u'\u239d'] + """ + if num_lines <= 0: + raise ValueError('number of lines must be positive') + elif num_lines == 1: + return [self.character] + elif num_lines == 2: + return [self.top_2, self.bottom_2] + elif num_lines == 3: + return [self.top, self.middle, self.bottom] + elif num_lines %2 == 0: + ext = [self.extension] * ((num_lines-4) // 2) + return [self.top] + ext + [self.middle_top, self.middle_bottom] + ext + [self.bottom] + else: # num_lines %2 == 1 + ext = [self.extension] * ((num_lines-3) // 2) + return [self.top] + ext + [self.middle] + ext + [self.bottom] + + def print_to_stdout(self, num_lines): + """ + Print the multi-line symbol + + This method is for testing purposes. + + INPUT: + + - ``num_lines`` -- integer. The total number of lines. + + EXAMPLES:: + + sage: from sage.typeset.symbols import * + sage: unicode_integral.print_to_stdout(1) + ∫ + sage: unicode_integral.print_to_stdout(2) + ⌠ + ⌡ + sage: unicode_integral.print_to_stdout(3) + ⌠ + ⎮ + ⌡ + sage: unicode_integral.print_to_stdout(4) + ⌠ + ⎮ + ⎮ + ⌡ + """ + print(u'\n'.join(self(num_lines))) + + +class CompoundAsciiSymbol(CompoundSymbol): + + def character_art(self, num_lines): + """ + Return the ASCII art of the symbol + + EXAMPLES:: + + sage: from sage.typeset.symbols import * + sage: ascii_left_curly_brace.character_art(3) + { + { + { + """ + from sage.typeset.ascii_art import AsciiArt + return AsciiArt(self(num_lines)) + + +class CompoundUnicodeSymbol(CompoundSymbol): + + def character_art(self, num_lines): + """ + Return the unicode art of the symbol + + EXAMPLES:: + + sage: from sage.typeset.symbols import * + sage: unicode_left_curly_brace.character_art(3) + ⎧ + ⎨ + ⎩ + """ + from sage.typeset.unicode_art import UnicodeArt + return UnicodeArt(self(num_lines)) + + +ascii_integral = CompoundAsciiSymbol( + 'int', + r' /\\', + r' | ', + r'\\/ ', +) + +unicode_integral = CompoundUnicodeSymbol( + unicodedata.lookup('INTEGRAL'), + unicodedata.lookup('TOP HALF INTEGRAL'), + unicodedata.lookup('INTEGRAL EXTENSION'), + unicodedata.lookup('BOTTOM HALF INTEGRAL'), +) + +ascii_left_parenthesis = CompoundAsciiSymbol( + '(', + '(', + '(', + '(', +) + +ascii_right_parenthesis = CompoundAsciiSymbol( + ')', + ')', + ')', + ')', +) + +unicode_left_parenthesis = CompoundUnicodeSymbol( + unicodedata.lookup('LEFT PARENTHESIS'), + unicodedata.lookup('LEFT PARENTHESIS UPPER HOOK'), + unicodedata.lookup('LEFT PARENTHESIS EXTENSION'), + unicodedata.lookup('LEFT PARENTHESIS LOWER HOOK'), +) + +unicode_right_parenthesis = CompoundUnicodeSymbol( + unicodedata.lookup('RIGHT PARENTHESIS'), + unicodedata.lookup('RIGHT PARENTHESIS UPPER HOOK'), + unicodedata.lookup('RIGHT PARENTHESIS EXTENSION'), + unicodedata.lookup('RIGHT PARENTHESIS LOWER HOOK'), +) + +ascii_left_square_bracket = CompoundAsciiSymbol( + '[', + '[', + '[', + '[', +) + +ascii_right_square_bracket = CompoundAsciiSymbol( + ']', + ']', + ']', + ']', +) + +unicode_left_square_bracket = CompoundUnicodeSymbol( + unicodedata.lookup('LEFT SQUARE BRACKET'), + unicodedata.lookup('LEFT SQUARE BRACKET UPPER CORNER'), + unicodedata.lookup('LEFT SQUARE BRACKET EXTENSION'), + unicodedata.lookup('LEFT SQUARE BRACKET LOWER CORNER'), +) + +unicode_right_square_bracket = CompoundUnicodeSymbol( + unicodedata.lookup('RIGHT SQUARE BRACKET'), + unicodedata.lookup('RIGHT SQUARE BRACKET UPPER CORNER'), + unicodedata.lookup('RIGHT SQUARE BRACKET EXTENSION'), + unicodedata.lookup('RIGHT SQUARE BRACKET LOWER CORNER'), +) + +ascii_left_curly_brace = CompoundAsciiSymbol( + '{', + '{', + '{', + '{', +) + +ascii_right_curly_brace = CompoundAsciiSymbol( + '}', + '}', + '}', + '}', +) + +unicode_left_curly_brace = CompoundUnicodeSymbol( + unicodedata.lookup('LEFT CURLY BRACKET'), + unicodedata.lookup('LEFT CURLY BRACKET UPPER HOOK'), + unicodedata.lookup('CURLY BRACKET EXTENSION'), + unicodedata.lookup('LEFT CURLY BRACKET LOWER HOOK'), + unicodedata.lookup('LEFT CURLY BRACKET MIDDLE PIECE'), + unicodedata.lookup('RIGHT CURLY BRACKET LOWER HOOK'), + unicodedata.lookup('RIGHT CURLY BRACKET UPPER HOOK'), + unicodedata.lookup('UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION'), + unicodedata.lookup('UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION'), +) + +unicode_right_curly_brace = CompoundUnicodeSymbol( + unicodedata.lookup('RIGHT CURLY BRACKET'), + unicodedata.lookup('RIGHT CURLY BRACKET UPPER HOOK'), + unicodedata.lookup('CURLY BRACKET EXTENSION'), + unicodedata.lookup('RIGHT CURLY BRACKET LOWER HOOK'), + unicodedata.lookup('RIGHT CURLY BRACKET MIDDLE PIECE'), + unicodedata.lookup('LEFT CURLY BRACKET LOWER HOOK'), + unicodedata.lookup('LEFT CURLY BRACKET UPPER HOOK'), + unicodedata.lookup('UPPER RIGHT OR LOWER LEFT CURLY BRACKET SECTION'), + unicodedata.lookup('UPPER LEFT OR LOWER RIGHT CURLY BRACKET SECTION'), +) + + + diff --git a/src/sage/typeset/unicode_art.py b/src/sage/typeset/unicode_art.py new file mode 100644 index 00000000000..c9ff9843f90 --- /dev/null +++ b/src/sage/typeset/unicode_art.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +r""" +Unicode Art + +This module implements ascii art using unicode characters. It is a +strict superset of :mod:`~sage.typeset.ascii_art`. +""" + +#******************************************************************************* +# Copyright (C) 2013 Jean-Baptiste Priez , +# 2015 Volker Braun +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# This code is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# The full text of the GPL is available at: +# +# http://www.gnu.org/licenses/ +#******************************************************************************* + +from sage.typeset.character_art import CharacterArt +from sage.typeset.character_art_factory import CharacterArtFactory +import sage.typeset.symbols as symbol + + +class UnicodeArt(CharacterArt): + r""" + An Ascii art object is an object with some specific representation for + *printing*. + + INPUT: + + - ``lines`` -- the list of lines of the representation of the ascii art + object + + - ``breakpoints`` -- the list of points where the representation can be + split + + - ``baseline`` -- the reference line (from the bottom) + + - ``atomic`` -- indicate if the ascii art representation is splittable + (must be coherent with breakpoints) + + EXAMPLES:: + + sage: i = var('i') + sage: unicode_art(sum(pi^i/factorial(i)*x^i, i, 0, oo)) + pi*x + e + """ + _string_type = unicode + + +_unicode_art_factory = CharacterArtFactory( + UnicodeArt, unicode, '_unicode_art_', + (symbol.unicode_left_parenthesis, symbol.unicode_right_parenthesis), + (symbol.unicode_left_square_bracket, symbol.unicode_right_square_bracket), + (symbol.unicode_left_curly_brace, symbol.unicode_right_curly_brace), +) + + +def unicode_art(obj): + r""" + Return an unicode art representation + + INPUT: + + - ``obj`` -- anything. The object whose unicode art representation + we want. + + OUTPUT: + + :class:`UnicodeArt` instance. + + EXAMPLES:: + + sage: unicode_art(integral(exp(x+x^2)/(x+1), x)) + / + | + | 2 + | x + x + | e + | ------- dx + | x + 1 + | + / + + TESTS:: + + sage: n = var('n') + sage: unicode_art(sum(binomial(2 * n, n + 1) * x^n, n, 0, oo)) + / __________ \ + -\2*x + \/ -4*x + 1 - 1/ + -------------------------- + __________ + 2*x*\/ -4*x + 1 + sage: unicode_art(list(DyckWords(3))) + ⎡ /\ ⎤ + ⎢ /\ /\ /\/\ / \ ⎥ + ⎣ /\/\/\, /\/ \, / \/\, / \, / \ ⎦ + sage: ascii_art(1) + 1 + """ + return _unicode_art_factory.build(obj) + + +empty_unicode_art = _unicode_art_factory.build_empty() + From 3ed994df5c11d31d9e25f46fd9ed745c5258fe44 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sat, 16 May 2015 22:35:04 +0200 Subject: [PATCH 02/12] Add a OutputUnicodeArt rich output type --- src/sage/matrix/matrix0.pyx | 47 ++++++------- src/sage/repl/ipython_extension.py | 12 ++-- src/sage/repl/rich_output/backend_base.py | 59 ++++++++++++++-- src/sage/repl/rich_output/backend_doctest.py | 7 +- src/sage/repl/rich_output/backend_ipython.py | 8 ++- src/sage/repl/rich_output/display_manager.py | 11 ++- src/sage/repl/rich_output/output_basic.py | 72 ++++++++++++++++++++ src/sage/repl/rich_output/output_catalog.py | 1 + src/sage/repl/rich_output/preferences.py | 2 +- src/sage/typeset/ascii_art.py | 31 +++++++-- src/sage/typeset/character_art_factory.py | 27 ++++++++ src/sage/typeset/unicode_art.py | 33 +++++++-- 12 files changed, 255 insertions(+), 55 deletions(-) diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index 662a42a1a6e..ba046dda78d 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -1880,35 +1880,28 @@ cdef class Matrix(sage.structure.element.Matrix): s = "\n".join(rows) return s -## def _latex_sparse(self, variable="x"): -## r""" -## Return a latex string that represents this matrix as a sparse -## matrix. The rows are printed as sums $\sum a_i x_i$, where -## $x$ is the variable. + def _unicode_art_(self): + """ + Unicode art representation of matrices -## EXAMPLES: + EXAMPLES:: -## """ -## cdef Py_ssize_t nr, nc, i, j -## nr = self._nrows -## nc = self._ncols -## s = "\\left(\\begin{align*}\n" -## for i from 0 <= i < nr: -## v = [] -## for j from 0 <= j < nc: -## x = self.get_unsafe(i, j) -## if x != 0: -## v.append((j, x)) -## for j from 0 <= j < len(v): -## s = s + "%s*%s_{%s}"%(v[j][1], variable, v[j][0]) -## if j == 0: -## s = s + "& + " -## elif j < len(v) - 1: -## s = s + " + " -## else: -## s = s + "\\\\\n" -## s = s + "\n\\end{align*}" -## return s + sage: A = matrix([[1,2], [3,4], [5,6]]) + sage: A._unicode_art_() + sage: unicode_art(A) # indirect doctest + + If the matrix is too big, don't print all of the elements:: + + sage: A = random_matrix(ZZ, 100) + sage: unicode_art(A) + 100 x 100 dense matrix over Integer Ring + """ + from sage.typeset.unicode_art import UnicodeArt + if self._nrows < max_rows and self._ncols < max_cols: + output = self.str(unicode=True) + else: + output = self.__repr__() + return UnicodeArt(output.splitlines()) def _latex_(self): r""" diff --git a/src/sage/repl/ipython_extension.py b/src/sage/repl/ipython_extension.py index 0dc2b243f9f..aeb7896d13c 100644 --- a/src/sage/repl/ipython_extension.py +++ b/src/sage/repl/ipython_extension.py @@ -269,7 +269,7 @@ def display(self, args): TESTS:: sage: shell.run_cell('%display invalid_mode') - value must be unset (None) or one of ('plain', 'ascii_art', 'latex'), got invalid_mode + value must be unset (None) or one of ('plain', 'ascii_art', 'unicode_art', 'latex'), got invalid_mode sage: shell.quit() """ from sage.repl.rich_output import get_display_manager @@ -284,17 +284,17 @@ def display(self, args): dm.preferences.text = 'plain' elif arg0 == 'typeset': dm.preferences.text = 'latex' - elif arg0 == 'ascii_art' and len(args) > 1: + elif arg0 in ['ascii_art', 'unicode_art'] and len(args) > 1: try: max_width = int(args[1]) except ValueError: max_width = 0 if max_width <= 0: raise ValueError( - "ascii_art max width must be a positive integer") - import sage.misc.ascii_art as ascii_art - ascii_art.MAX_WIDTH = max_width - dm.preferences.text = 'ascii_art' + "max width must be a positive integer") + import sage.typeset.character_art as character_art + character_art.MAX_WIDTH = max_width + dm.preferences.text = arg0 # Unset all elif arg0 in ['default', 'None']: # un-stringify "%display None" for option in map(str, dm.preferences.available_options()): diff --git a/src/sage/repl/rich_output/backend_base.py b/src/sage/repl/rich_output/backend_base.py index 1a5f6c915ae..7b5a4758585 100644 --- a/src/sage/repl/rich_output/backend_base.py +++ b/src/sage/repl/rich_output/backend_base.py @@ -349,18 +349,65 @@ def ascii_art_formatter(self, obj, **kwds): sage: backend.ascii_art_formatter([1,2,3], concatenate=True ).ascii_art.get() '1 2 3' """ - from sage.misc.ascii_art import ascii_art, empty_ascii_art + from sage.typeset.ascii_art import ascii_art, empty_ascii_art if kwds.get('concatenate', False): - result = empty_ascii_art - for o in obj: - if result is not empty_ascii_art: - result += ascii_art(' ') - result += ascii_art(o) + result = ascii_art(*obj, sep=' ') else: result = ascii_art(obj) from sage.repl.rich_output.output_basic import OutputAsciiArt return OutputAsciiArt(str(result)) + def unicode_art_formatter(self, obj, **kwds): + r""" + Hook to override how unicode art is being formatted. + + INPUT: + + - ``obj`` -- anything. + + - ``**kwds`` -- optional keyword arguments to control the + formatting. Supported are: + + * ``concatenate`` -- boolean (default: ``False``). If + ``True``, the argument ``obj`` must be iterable and its + entries will be concatenated. There is a single + whitespace between entries. + + OUTPUT: + + Instance of + :class:`~sage.repl.rich_output.output_basic.OutputUnicodeArt` + containing the unicode art string representation of the object. + + EXAMPLES:: + + sage: from sage.repl.rich_output.backend_base import BackendBase + sage: backend = BackendBase() + sage: out = backend.unicode_art_formatter(range(30)) + sage: out + OutputUnicodeArt container + sage: out.unicode_art + buffer containing 236 bytes + sage: print(out.unicode_art.get()) + ⎡ + ⎣ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + + ⎤ + 22, 23, 24, 25, 26, 27, 28, 29 ⎦ + + sage: backend.unicode_art_formatter([1,2,3], concatenate=False).unicode_art.get() + '\xe2\x8e\xa1 \xe2\x8e\xa4\n\xe2\x8e\xa3 1, 2, 3 \xe2\x8e\xa6' + sage: backend.unicode_art_formatter([1,2,3], concatenate=True ).unicode_art.get() + '1 2 3' + """ + from sage.typeset.unicode_art import unicode_art, empty_unicode_art + if kwds.get('concatenate', False): + result = unicode_art(*obj, sep=' ') + else: + result = unicode_art(obj) + from sage.repl.rich_output.output_basic import OutputUnicodeArt + return OutputUnicodeArt(str(result)) + def latex_formatter(self, obj, **kwds): r""" Hook to override how Latex is being formatted. diff --git a/src/sage/repl/rich_output/backend_doctest.py b/src/sage/repl/rich_output/backend_doctest.py index 86bf6dea437..e2f98c6c404 100644 --- a/src/sage/repl/rich_output/backend_doctest.py +++ b/src/sage/repl/rich_output/backend_doctest.py @@ -133,7 +133,7 @@ def supported_output(self): True """ return set([ - OutputPlainText, OutputAsciiArt, OutputLatex, + OutputPlainText, OutputAsciiArt, OutputUnicodeArt, OutputLatex, OutputImagePng, OutputImageGif, OutputImageJpg, OutputImageSvg, OutputImagePdf, OutputImageDvi, OutputSceneJmol, OutputSceneCanvas3d, OutputSceneWavefront, @@ -204,9 +204,10 @@ def display_immediately(self, plain_text, rich_output): sage: dm.display_immediately(plt) # indirect doctest """ self.validate(rich_output) + types_to_print = [OutputPlainText, OutputAsciiArt, OutputUnicodeArt] if isinstance(rich_output, OutputLatex): print(rich_output.mathjax(display=False)) - elif any(isinstance(rich_output, cls) for cls in [OutputPlainText, OutputAsciiArt]): + elif any(isinstance(rich_output, cls) for cls in types_to_print): rich_output.print_to_stdout() def validate(self, rich_output): @@ -250,6 +251,8 @@ def validate(self, rich_output): pass elif isinstance(rich_output, OutputAsciiArt): pass + elif isinstance(rich_output, OutputUnicodeArt): + pass elif isinstance(rich_output, OutputLatex): assert rich_output.mathjax().startswith('') elif isinstance(rich_output, OutputImagePng): diff --git a/src/sage/repl/rich_output/backend_ipython.py b/src/sage/repl/rich_output/backend_ipython.py index 0eac0af8e91..f34026056e3 100644 --- a/src/sage/repl/rich_output/backend_ipython.py +++ b/src/sage/repl/rich_output/backend_ipython.py @@ -187,7 +187,7 @@ def supported_output(self): True """ return set([ - OutputPlainText, OutputAsciiArt, OutputLatex, + OutputPlainText, OutputAsciiArt, OutputUnicodeArt, OutputLatex, OutputImagePng, OutputImageGif, OutputImagePdf, OutputImageDvi, OutputSceneJmol, OutputSceneWavefront, @@ -230,6 +230,8 @@ def displayhook(self, plain_text, rich_output): return ({u'text/plain': rich_output.text.get()}, {}) elif isinstance(rich_output, OutputAsciiArt): return ({u'text/plain': rich_output.ascii_art.get()}, {}) + elif isinstance(rich_output, OutputUnicodeArt): + return ({u'text/plain': rich_output.unicode_art.get()}, {}) elif isinstance(rich_output, OutputLatex): return ({u'text/plain': rich_output.latex.get()}, {}) elif isinstance(rich_output, OutputImagePng): @@ -449,7 +451,7 @@ def supported_output(self): False """ return set([ - OutputPlainText, OutputAsciiArt, OutputLatex, + OutputPlainText, OutputAsciiArt, OutputUnicodeArt, OutputLatex, OutputImagePng, OutputImageJpg, OutputImageSvg, OutputImagePdf, OutputSceneJmol, @@ -492,6 +494,8 @@ def displayhook(self, plain_text, rich_output): return ({u'text/plain': rich_output.text.get()}, {}) elif isinstance(rich_output, OutputAsciiArt): return ({u'text/plain': rich_output.ascii_art.get()}, {}) + elif isinstance(rich_output, OutputUnicodeArt): + return ({u'text/plain': rich_output.unicode_art.get()}, {}) elif isinstance(rich_output, OutputLatex): return ({u'text/html': rich_output.mathjax(), u'text/plain': plain_text.text.get(), diff --git a/src/sage/repl/rich_output/display_manager.py b/src/sage/repl/rich_output/display_manager.py index 036f0421bd9..4e2c35cc27d 100644 --- a/src/sage/repl/rich_output/display_manager.py +++ b/src/sage/repl/rich_output/display_manager.py @@ -38,7 +38,7 @@ from sage.structure.sage_object import SageObject from sage.repl.rich_output.output_basic import ( - OutputPlainText, OutputAsciiArt, OutputLatex, + OutputPlainText, OutputAsciiArt, OutputUnicodeArt, OutputLatex, ) from sage.repl.rich_output.preferences import DisplayPreferences @@ -489,6 +489,10 @@ def _preferred_text_formatter(self, obj, plain_text=None, **kwds): sage: dm._preferred_text_formatter([1/42]) OutputAsciiArt container + sage: dm.preferences.text = 'unicode_art' + sage: dm._preferred_text_formatter([1/42]) + OutputUnicodeArt container + sage: dm.preferences.text = 'latex' sage: dm._preferred_text_formatter([1/42]) \newcommand{\Bold}[1]{\mathbf{#1}}\verb|OutputLatex|\phantom{\verb!x!}\verb|container| @@ -502,6 +506,11 @@ def _preferred_text_formatter(self, obj, plain_text=None, **kwds): if type(out) != OutputAsciiArt: raise OutputTypeException('backend returned wrong output type, require AsciiArt') return out + if want == 'unicode_art' and OutputUnicodeArt in supported: + out = self._backend.unicode_art_formatter(obj, **kwds) + if type(out) != OutputUnicodeArt: + raise OutputTypeException('backend returned wrong output type, require UnicodeArt') + return out if want == 'latex' and OutputLatex in supported: out = self._backend.latex_formatter(obj, **kwds) if type(out) != OutputLatex: diff --git a/src/sage/repl/rich_output/output_basic.py b/src/sage/repl/rich_output/output_basic.py index dd01012f84a..1557624d65b 100644 --- a/src/sage/repl/rich_output/output_basic.py +++ b/src/sage/repl/rich_output/output_basic.py @@ -221,6 +221,78 @@ def print_to_stdout(self): print(self.ascii_art.get()) +class OutputUnicodeArt(OutputBase): + + def __init__(self, unicode_art): + """ + Unicode Art Output + + Similar to :class:`OutputAsciiArt` but using the entire + unicode range. + + INPUT: + + - ``unicode_art`` -- + :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively, + a string (unicode in Python 2.x) can be passed directly + which will then be converted into an + :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Unicode + art rendered into a string. + + EXAMPLES:: + + sage: from sage.repl.rich_output.output_catalog import OutputUnicodeArt + sage: OutputUnicodeArt(u':-}') + OutputUnicodeArt container + """ + # Internally, all buffers store bytes. Unicode is always utf-8 + # encoded. + if isinstance(unicode_art, unicode): + unicode_art = unicode_art.encode('utf-8') + self.unicode_art = OutputBuffer(unicode_art) + + @classmethod + def example(cls): + r""" + Construct a sample unicode art output container + + This static method is meant for doctests, so they can easily + construt an example. + + OUTPUT: + + An instance of :class:`OutputUnicodeArt`. + + EXAMPLES:: + + sage: from sage.repl.rich_output.output_catalog import OutputUnicodeArt + sage: OutputUnicodeArt.example() + OutputUnicodeArt container + sage: OutputUnicodeArt.example().unicode_art.get() + '\xe2\x8e\x9b-11 0 1\xe2\x8e\x9e\n\xe2\x8e\x9c 3 -1 0\xe2\x8e\x9f\n\xe2\x8e\x9d -1 -1 0\xe2\x8e\xa0' + """ + return cls(u'⎛-11 0 1⎞\n' + u'⎜ 3 -1 0⎟\n' + u'⎝ -1 -1 0⎠') + + def print_to_stdout(self): + """ + Write the data to stdout. + + This is just a convenience method to help with debugging. + + EXAMPLES:: + + sage: from sage.repl.rich_output.output_catalog import OutputUnicodeArt + sage: unicode_art = OutputUnicodeArt.example() + sage: unicode_art.print_to_stdout() + ⎛-11 0 1⎞ + ⎜ 3 -1 0⎟ + ⎝ -1 -1 0⎠ + """ + print(self.unicode_art.get()) + + class OutputLatex(OutputBase): def __init__(self, latex): diff --git a/src/sage/repl/rich_output/output_catalog.py b/src/sage/repl/rich_output/output_catalog.py index 533f4d1a2d9..f0fcc3379a8 100644 --- a/src/sage/repl/rich_output/output_catalog.py +++ b/src/sage/repl/rich_output/output_catalog.py @@ -18,6 +18,7 @@ from .output_basic import ( OutputPlainText, OutputAsciiArt, + OutputUnicodeArt, OutputLatex, ) diff --git a/src/sage/repl/rich_output/preferences.py b/src/sage/repl/rich_output/preferences.py index ba7c4d988ba..5af0ff6d89f 100644 --- a/src/sage/repl/rich_output/preferences.py +++ b/src/sage/repl/rich_output/preferences.py @@ -399,7 +399,7 @@ class DisplayPreferences(PreferencesABC): DisplayPreferences._add_option( 'text', - ('plain', 'ascii_art', 'latex'), + ('plain', 'ascii_art', 'unicode_art', 'latex'), """ Which textual representation is preferred """ diff --git a/src/sage/typeset/ascii_art.py b/src/sage/typeset/ascii_art.py index 01e3c464b63..298b8273d86 100644 --- a/src/sage/typeset/ascii_art.py +++ b/src/sage/typeset/ascii_art.py @@ -193,14 +193,22 @@ class AsciiArt(CharacterArt): ) -def ascii_art(obj): +empty_ascii_art = _ascii_art_factory.build_empty() + + +def ascii_art(*obj, **kwds): r""" Return an ASCII art representation INPUT: - - ``obj`` -- anything. The object whose ascii art representation - we want. + - ``*obj`` -- any number of positional arguments, of arbitrary + type. The objects whose ascii art representation we want. + + - ``sep`` -- optional ``'sep=...'`` keyword argument. Anything + that can be converted to ascii art (default: empty ascii + art). The separator in-between a list of objects. Only used if + more than one object given. OUTPUT: @@ -219,6 +227,12 @@ def ascii_art(obj): | / + sage: ident = lambda n: identity_matrix(ZZ, n) + sage: ascii_art(ident(1), ident(2), ident(3), sep=' : ') + [1 0 0] + [1 0] [0 1 0] + [1] : [0 1] : [0 0 1] + TESTS:: sage: n = var('n') @@ -235,8 +249,15 @@ def ascii_art(obj): sage: ascii_art(1) 1 """ - return _ascii_art_factory.build(obj) + separator = kwds.pop('sep', empty_ascii_art) + if kwds: + raise ValueError('unknown keyword arguments: {0}'.format(kwds.keys())) + if len(obj) == 1: + return _ascii_art_factory.build(obj[0]) + if not isinstance(separator, AsciiArt): + separator = _ascii_art_factory.build(separator) + obj = map(_ascii_art_factory.build, obj) + return _ascii_art_factory.concatenate(obj, separator, empty_ascii_art) -empty_ascii_art = _ascii_art_factory.build_empty() diff --git a/src/sage/typeset/character_art_factory.py b/src/sage/typeset/character_art_factory.py index 37e74edf328..826435feb22 100644 --- a/src/sage/typeset/character_art_factory.py +++ b/src/sage/typeset/character_art_factory.py @@ -322,3 +322,30 @@ def build_tuple(self, tuple): return self.build_container( repr_elems, self.left_parenthesis, self.right_parenthesis) + def concatenate(self, iterable, separator, empty): + """ + Concatenate multiple character art instances + + INPUT: + + - ``iterable`` -- iterable of character art. + + - ``separable`` -- character art. The separator in-between the + iterable. + + - ``empty`` -- the empty character art. + + EXAMPLES:: + + sage: i2 = identity_matrix(ZZ, 2) + sage: ascii_art(i2, i2, i2, sep=ascii_art(1/x)) + 1 1 + [1 0]-[1 0]-[1 0] + [0 1]x[0 1]x[0 1] + """ + result = empty + for obj in iterable: + if result is not empty: + result += separator + result += obj + return result diff --git a/src/sage/typeset/unicode_art.py b/src/sage/typeset/unicode_art.py index c9ff9843f90..872a4319983 100644 --- a/src/sage/typeset/unicode_art.py +++ b/src/sage/typeset/unicode_art.py @@ -63,14 +63,23 @@ class UnicodeArt(CharacterArt): ) -def unicode_art(obj): +empty_unicode_art = _unicode_art_factory.build_empty() + + +def unicode_art(*obj, **kwds): r""" Return an unicode art representation INPUT: - - ``obj`` -- anything. The object whose unicode art representation - we want. + + - ``*obj`` -- any number of positional arguments, of arbitrary + type. The objects whose ascii art representation we want. + + - ``sep`` -- optional ``'sep=...'`` keyword argument. Anything + that can be converted to unicode art (default: empty unicode + art). The separator in-between a list of objects. Only used if + more than one object given. OUTPUT: @@ -89,6 +98,12 @@ def unicode_art(obj): | / + sage: ident = lambda n: identity_matrix(ZZ, n) + sage: unicode_art(ident(1), ident(2), ident(3), sep=' : ') + ⎛1 0 0⎞ + ⎛1 0⎞ ⎜0 1 0⎟ + (1) : ⎝0 1⎠ : ⎝0 0 1⎠ + TESTS:: sage: n = var('n') @@ -105,8 +120,16 @@ def unicode_art(obj): sage: ascii_art(1) 1 """ - return _unicode_art_factory.build(obj) + separator = kwds.pop('sep', empty_unicode_art) + if kwds: + raise ValueError('unknown keyword arguments: {0}'.format(kwds.keys())) + if len(obj) == 1: + return _unicode_art_factory.build(obj[0]) + if not isinstance(separator, UnicodeArt): + separator = _unicode_art_factory.build(separator) + obj = map(_unicode_art_factory.build, obj) + return _unicode_art_factory.concatenate(obj, separator, empty_unicode_art) + -empty_unicode_art = _unicode_art_factory.build_empty() From 7616a9471136d2399ef3a0192c62d33c5a6da19b Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 17 May 2015 00:52:14 +0200 Subject: [PATCH 03/12] Trac 18357: unicode art for Dyck words and Tableaux --- src/sage/combinat/dyck_word.py | 32 ++++++++-- src/sage/combinat/tableau.py | 111 +++++++++++++++++++++++++++++---- 2 files changed, 128 insertions(+), 15 deletions(-) diff --git a/src/sage/combinat/dyck_word.py b/src/sage/combinat/dyck_word.py index 547cb65883a..0f2e02f5605 100644 --- a/src/sage/combinat/dyck_word.py +++ b/src/sage/combinat/dyck_word.py @@ -614,6 +614,20 @@ def _ascii_art_(self): ret = self._repr_lattice() return AsciiArt(ret.splitlines(), baseline=0) + def _unicode_art_(self): + r""" + Return an unicode art representation of this Dyck word. + + EXAMPLES:: + + sage: unicode_art(list(DyckWords(3))) + ⎡ ╱╲ ⎤ + ⎢ ╱╲ ╱╲ ╱╲╱╲ ╱ ╲ ⎥ + ⎣ ╱╲╱╲╱╲, ╱╲╱ ╲, ╱ ╲╱╲, ╱ ╲, ╱ ╲ ⎦ + """ + from sage.typeset.unicode_art import UnicodeArt + return UnicodeArt(self.to_path_string(unicode=True).splitlines()) + def __str__(self): r""" Return a string consisting of matched parentheses corresponding to @@ -631,7 +645,7 @@ def __str__(self): else: return "".join(map(replace_symbols, [x for x in self])) - def to_path_string(self): + def to_path_string(self, unicode=False): r""" A path representation of the Dyck word consisting of steps ``/`` and ``\`` . @@ -648,15 +662,25 @@ def to_path_string(self): /\/ \/\/\ / \ """ - res = [([" "]*len(self)) for _ in range(self.height())] + if unicode: + import unicodedata + space = u' ' + up = unicodedata.lookup('BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT') + down = unicodedata.lookup('BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT') + else: + space = ' ' + up = '/' + down = '\\' + + res = [([space]*len(self)) for _ in range(self.height())] h = 1 for i, p in enumerate(self): if p == open_symbol: - res[-h][i] = "/" + res[-h][i] = up h += 1 else: h -= 1 - res[-h][i] = "\\" + res[-h][i] = down return "\n".join("".join(l) for l in res) def pretty_print(self, type=None, labelling=None, underpath=True): diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index a33db9c4614..17956fbb902 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -519,9 +519,28 @@ def _ascii_art_(self): from sage.misc.ascii_art import AsciiArt return AsciiArt(ascii.splitlines()) + def _unicode_art_(self): + r""" + TESTS:: + + sage: unicode_art(Tableau([[1,2,3],[4],[5]])) + ┌───┬───┬───┐ + │ 1 │ 2 │ 3 │ + ├───┼───┴───┘ + │ 4 │ + ├───┤ + │ 5 │ + └───┘ + sage: unicode_art(Tableau([])) + ┌┐ + └┘ + """ + from sage.typeset.unicode_art import UnicodeArt + return UnicodeArt(self._ascii_art_table(unicode=True).splitlines()) + _ascii_art_repr = _repr_diagram - def _ascii_art_table(self): + def _ascii_art_table(self, unicode=False): """ TESTS: @@ -578,9 +597,54 @@ def _ascii_art_table(self): | 1 | 2 | 15 | 7 | +----+----+----+---+ sage: Tableaux.global_options.reset() + + Unicode version:: + + sage: t = Tableau([[1,2,15,7],[12,5],[8,10],[9]]) + sage: print t._ascii_art_table(unicode=True) + ┌────┬────┬────┬───┐ + │ 1 │ 2 │ 15 │ 7 │ + ├────┼────┼────┴───┘ + │ 12 │ 5 │ + ├────┼────┤ + │ 8 │ 10 │ + ├────┼────┘ + │ 9 │ + └────┘ + sage: Tableaux().global_options(convention='french') + sage: t = Tableau([[1,2,15,7],[12,5],[8,10],[9]]) + sage: print t._ascii_art_table(unicode=True) + ┌────┐ + │ 9 │ + ├────┼────┐ + │ 8 │ 10 │ + ├────┼────┤ + │ 12 │ 5 │ + ├────┼────┼────┬───┐ + │ 1 │ 2 │ 15 │ 7 │ + └────┴────┴────┴───┘ + sage: Tableaux.global_options.reset() """ + if unicode: + import unicodedata + v = unicodedata.lookup('BOX DRAWINGS LIGHT VERTICAL') + h = unicodedata.lookup('BOX DRAWINGS LIGHT HORIZONTAL') + dl = unicodedata.lookup('BOX DRAWINGS LIGHT DOWN AND LEFT') + dr = unicodedata.lookup('BOX DRAWINGS LIGHT DOWN AND RIGHT') + ul = unicodedata.lookup('BOX DRAWINGS LIGHT UP AND LEFT') + ur = unicodedata.lookup('BOX DRAWINGS LIGHT UP AND RIGHT') + vr = unicodedata.lookup('BOX DRAWINGS LIGHT VERTICAL AND RIGHT') + vl = unicodedata.lookup('BOX DRAWINGS LIGHT VERTICAL AND LEFT') + uh = unicodedata.lookup('BOX DRAWINGS LIGHT UP AND HORIZONTAL') + dh = unicodedata.lookup('BOX DRAWINGS LIGHT DOWN AND HORIZONTAL') + vh = unicodedata.lookup('BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL') + else: + v = '|' + h = '-' + dl = dr = ul = ur = vr = vl = uh = dh = vh = '+' + if len(self) == 0: - return "++\n++" + return dr + dl + '\n' + ur + ul # Get the widths of the columns str_tab = map(lambda row: map(str, row), self) @@ -591,22 +655,47 @@ def _ascii_art_table(self): matr = [] # just the list of lines l1 = "" - for w in col_widths: - l1 += "+--" + '-'*w - matr.append(l1 + "+") - for row in str_tab: + l1 += dr + h*(2+col_widths[0]) + for w in col_widths[1:]: + l1 += dh + h + h + h*w + matr.append(l1 + dl) + for nrow,row in enumerate(str_tab): l1 = ""; l2 = "" - for i,e in enumerate(row): - l1 += "+--" + '-'*col_widths[i] - l2 += "| {:^{width}} ".format(e, width=col_widths[i]) - l1 += "+"; l2 += "|" + n = len(str_tab[nrow+1]) if nrow+1 < len(str_tab) else 0 + for i,(e,w) in enumerate(zip(row,col_widths)): + if i == 0: + if n: + l1 += vr + h*(2+w) + else: + l1 += ur + h*(2+w) + elif i <= n: + l1 += vh + h*(2+w) + else: + l1 += uh + h*(2+w) + if unicode: + l2 += u"{} {:^{width}} ".format(v, e, width=w) + else: + l2 += "{} {:^{width}} ".format(v, e, width=w) + if i+1 <= n: + l1 += vl + else: + l1 += ul + l2 += v matr.append(l2) matr.append(l1) if self.parent().global_options('convention') == "English": return "\n".join(matr) else: - return "\n".join(reversed(matr)) + output = "\n".join(reversed(matr)) + if unicode: + tr = { + ord(dl): ul, ord(dr): ur, + ord(ul): dl, ord(ur): dr, + ord(dh): uh, ord(uh): dh} + return output.translate(tr) + else: + return output def _ascii_art_compact(self): """ From e41f4f66e36f4c23d77126f567da42b3eb78f599 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sun, 17 May 2015 01:11:47 +0200 Subject: [PATCH 04/12] Always use unicode for IPython backend --- src/sage/repl/rich_output/backend_ipython.py | 54 ++++++++++++-------- src/sage/repl/rich_output/buffer.py | 19 +++++++ src/sage/repl/rich_output/output_basic.py | 11 ++-- src/sage/typeset/character_art_factory.py | 18 ++++++- 4 files changed, 76 insertions(+), 26 deletions(-) diff --git a/src/sage/repl/rich_output/backend_ipython.py b/src/sage/repl/rich_output/backend_ipython.py index f34026056e3..3c287683a1d 100644 --- a/src/sage/repl/rich_output/backend_ipython.py +++ b/src/sage/repl/rich_output/backend_ipython.py @@ -224,37 +224,49 @@ def displayhook(self, plain_text, rich_output): sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline sage: backend = BackendIPythonCommandline() sage: backend.displayhook(plain_text, plain_text) - ({u'text/plain': 'Example plain text output'}, {}) + ({u'text/plain': u'Example plain text output'}, {}) + + TESTS: + + We verify that unicode strings work:: + + sage: class Foo(sage.structure.sage_object.SageObject): + ....: def _rich_repr_(self, dm): + ....: return dm.types.OutputPlainText(u'Motörhead') + sage: from sage.repl.rich_output import get_display_manager + sage: dm = get_display_manager() + sage: dm.displayhook(Foo()) + ({u'text/plain': u'Mot\xc3\xb6rhead'}, {}) """ if isinstance(rich_output, OutputPlainText): - return ({u'text/plain': rich_output.text.get()}, {}) + return ({u'text/plain': rich_output.text.get_unicode()}, {}) elif isinstance(rich_output, OutputAsciiArt): - return ({u'text/plain': rich_output.ascii_art.get()}, {}) + return ({u'text/plain': rich_output.ascii_art.get_unicode()}, {}) elif isinstance(rich_output, OutputUnicodeArt): - return ({u'text/plain': rich_output.unicode_art.get()}, {}) + return ({u'text/plain': rich_output.unicode_art.get_unicode()}, {}) elif isinstance(rich_output, OutputLatex): - return ({u'text/plain': rich_output.latex.get()}, {}) + return ({u'text/plain': rich_output.latex.get_unicode()}, {}) elif isinstance(rich_output, OutputImagePng): msg = self.launch_viewer( - rich_output.png.filename(ext='png'), plain_text.text.get()) + rich_output.png.filename(ext='png'), plain_text.text.get_unicode()) return ({u'text/plain': msg}, {}) elif isinstance(rich_output, OutputImageGif): msg = self.launch_viewer( - rich_output.gif.filename(ext='gif'), plain_text.text.get()) + rich_output.gif.filename(ext='gif'), plain_text.text.get_unicode()) return ({u'text/plain': msg}, {}) elif isinstance(rich_output, OutputImagePdf): msg = self.launch_viewer( - rich_output.pdf.filename(ext='pdf'), plain_text.text.get()) + rich_output.pdf.filename(ext='pdf'), plain_text.text.get_unicode()) return ({u'text/plain': msg}, {}) elif isinstance(rich_output, OutputImageDvi): msg = self.launch_viewer( - rich_output.dvi.filename(ext='dvi'), plain_text.text.get()) + rich_output.dvi.filename(ext='dvi'), plain_text.text.get_unicode()) return ({u'text/plain': msg}, {}) elif isinstance(rich_output, OutputSceneJmol): - msg = self.launch_jmol(rich_output, plain_text.text.get()) + msg = self.launch_jmol(rich_output, plain_text.text.get_unicode()) return ({u'text/plain': msg}, {}) elif isinstance(rich_output, OutputSceneWavefront): - msg = self.launch_sage3d(rich_output, plain_text.text.get()) + msg = self.launch_sage3d(rich_output, plain_text.text.get_unicode()) return ({u'text/plain': msg}, {}) else: raise TypeError('rich_output type not supported') @@ -488,40 +500,40 @@ def displayhook(self, plain_text, rich_output): sage: from sage.repl.rich_output.backend_ipython import BackendIPythonNotebook sage: backend = BackendIPythonNotebook() sage: backend.displayhook(plain_text, plain_text) - ({u'text/plain': 'Example plain text output'}, {}) + ({u'text/plain': u'Example plain text output'}, {}) """ if isinstance(rich_output, OutputPlainText): - return ({u'text/plain': rich_output.text.get()}, {}) + return ({u'text/plain': rich_output.text.get_unicode()}, {}) elif isinstance(rich_output, OutputAsciiArt): - return ({u'text/plain': rich_output.ascii_art.get()}, {}) + return ({u'text/plain': rich_output.ascii_art.get_unicode()}, {}) elif isinstance(rich_output, OutputUnicodeArt): - return ({u'text/plain': rich_output.unicode_art.get()}, {}) + return ({u'text/plain': rich_output.unicode_art.get_unicode()}, {}) elif isinstance(rich_output, OutputLatex): return ({u'text/html': rich_output.mathjax(), - u'text/plain': plain_text.text.get(), + u'text/plain': plain_text.text.get_unicode(), }, {}) elif isinstance(rich_output, OutputImagePng): return ({u'image/png': rich_output.png.get(), - u'text/plain': plain_text.text.get(), + u'text/plain': plain_text.text.get_unicode(), }, {}) elif isinstance(rich_output, OutputImageJpg): return ({u'image/jpeg': rich_output.jpg.get(), - u'text/plain': plain_text.text.get(), + u'text/plain': plain_text.text.get_unicode(), }, {}) elif isinstance(rich_output, OutputImageSvg): return ({u'image/svg+xml': rich_output.svg.get(), - u'text/plain': plain_text.text.get(), + u'text/plain': plain_text.text.get_unicode(), }, {}) elif isinstance(rich_output, OutputImagePdf): return ({u'image/png': rich_output.png.get(), - u'text/plain': plain_text.text.get(), + u'text/plain': plain_text.text.get_unicode(), }, {}) elif isinstance(rich_output, OutputSceneJmol): from sage.repl.display.jsmol_iframe import JSMolHtml jsmol = JSMolHtml(rich_output, height=500) return ({u'text/html': jsmol.iframe(), - u'text/plain': plain_text.text.get(), + u'text/plain': plain_text.text.get_unicode(), }, {}) else: raise TypeError('rich_output type not supported') diff --git a/src/sage/repl/rich_output/buffer.py b/src/sage/repl/rich_output/buffer.py index 2526d71aa59..75c2d8c24eb 100644 --- a/src/sage/repl/rich_output/buffer.py +++ b/src/sage/repl/rich_output/buffer.py @@ -177,6 +177,25 @@ def get(self): self._data = f.read() return self._data + def get_unicode(self): + """ + Return the buffer content as string + + OUTPUT: + + String. Unicode in Python 2.x. Raises a ``UnicodeEncodeError`` + if the data is not valid utf-8. + + EXAMPLES:: + + sage: from sage.repl.rich_output.buffer import OutputBuffer + sage: OutputBuffer('test1234').get() + 'test1234' + sage: OutputBuffer('test1234').get_unicode() + u'test1234' + """ + return self.get().decode('utf-8') + def filename(self, ext=None): """ Return the filename. diff --git a/src/sage/repl/rich_output/output_basic.py b/src/sage/repl/rich_output/output_basic.py index 1557624d65b..656b1c08a05 100644 --- a/src/sage/repl/rich_output/output_basic.py +++ b/src/sage/repl/rich_output/output_basic.py @@ -101,8 +101,9 @@ def __init__(self, plain_text): - ``plain_text`` -- :class:`~sage.repl.rich_output.buffer.OutputBuffer`. Alternatively, - a string (bytes) can be passed directly which will then be - converted into an + a bytes (string in Python 2.x) or string (unicode in Python + 2.x) can be passed directly which will then be converted + into an :class:`~sage.repl.rich_output.buffer.OutputBuffer`. The plain text output. @@ -116,7 +117,11 @@ def __init__(self, plain_text): sage: OutputPlainText('foo') OutputPlainText container """ - self.text = OutputBuffer(plain_text) + # Internally, all buffers store bytes. Strings/Unicode is always utf-8 + # encoded. + if isinstance(plain_text, unicode): + plain_text = plain_text.encode('utf-8') + self.text = OutputBuffer(plain_text) @classmethod def example(cls): diff --git a/src/sage/typeset/character_art_factory.py b/src/sage/typeset/character_art_factory.py index 826435feb22..dd7b1de8b2c 100644 --- a/src/sage/typeset/character_art_factory.py +++ b/src/sage/typeset/character_art_factory.py @@ -158,6 +158,10 @@ def build_from_string(self, obj): """ Return the character art object created from splitting the object's string representation + INPUT: + + - ``obj`` -- utf-8 encoded byte string or unicode. + OUTPUT: Character art instance. @@ -172,9 +176,19 @@ def build_from_string(self, obj): ccccccccc sage: type(out) + + TESTS:: + + sage: factory.build_from_string(u'a\nbb\nccc') # same with unicode + a + bb + ccc """ - lines = self.string_type(obj).splitlines() - return self.art_type(lines) + if self.string_type is unicode and not isinstance(obj, unicode): + obj = str(obj).decode('utf-8') + if self.string_type is str and not isinstance(obj, str): + obj = unicode(obj).encode('utf-8') + return self.art_type(obj.splitlines()) def build_comma_sequence(self, iterable, func=None): r""" From 9a2405a1f40b89fbb87dcd56e770c5f7e9afc3b5 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sun, 17 May 2015 13:58:32 +0200 Subject: [PATCH 05/12] use named constants instead of literals for matrix unicode art --- src/sage/matrix/matrix0.pyx | 46 ++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index ba046dda78d..177ab06ec5c 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -1802,13 +1802,47 @@ cdef class Matrix(sage.structure.element.Matrix): # - crossing lines (cl) if shape is None: shape = "round" if unicode else "square" + if unicode: + import unicodedata + hl = unicodedata.lookup('BOX DRAWINGS LIGHT HORIZONTAL') + vl = unicodedata.lookup('BOX DRAWINGS LIGHT VERTICAL') + cl = unicodedata.lookup('BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL') + else: + hl = '-' # - horizontal line + vl = '|' # - vertical line + cl = '+' # - crossing lines if shape == "square": - symbols = u"⎡⎢⎣[⎤⎥⎦]│─┼" if unicode else "[[[[]]]]|-+" + if unicode: + from sage.typeset.symbols import ( + unicode_left_square_bracket as left, + unicode_right_square_bracket as right + ) + else: + from sage.typeset.symbols import ( + ascii_left_square_bracket as left, + ascii_right_square_bracket as right + ) elif shape == "round": - symbols = u"⎛⎜⎝(⎞⎟⎠)│─┼" if unicode else "(((())))|-+" + if unicode: + from sage.typeset.symbols import ( + unicode_left_parenthesis as left, + unicode_right_parenthesis as right + ) + else: + from sage.typeset.symbols import ( + ascii_left_parenthesis as left, + ascii_right_parenthesis as right + ) else: raise ValueError("No such shape") - tlb, mlb, blb, slb, trb, mrb, brb, srb, vl, hl, cl = symbols + tlb = left.top # - top left bracket + mlb = left.extension # - extension piece left bracket + blb = left.bottom # - bottom left bracket + slb = left.character # - single-row left bracket + trb = right.top # - top right bracket + mrb = right.extension # - extension piece right bracket + brb = right.bottom # - bottom right bracket + srb = right.character # - single-row right bracket if nr == 0 or nc == 0: return slb + srb @@ -1888,7 +1922,13 @@ cdef class Matrix(sage.structure.element.Matrix): sage: A = matrix([[1,2], [3,4], [5,6]]) sage: A._unicode_art_() + ⎛1 2⎞ + ⎜3 4⎟ + ⎝5 6⎠ sage: unicode_art(A) # indirect doctest + ⎛1 2⎞ + ⎜3 4⎟ + ⎝5 6⎠ If the matrix is too big, don't print all of the elements:: From dcb9e4acdd0198867385437f917751009bd362cd Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sun, 17 May 2015 15:53:08 +0200 Subject: [PATCH 06/12] fix broken tests and docs --- src/sage/combinat/shuffle.py | 12 ++++++------ src/sage/combinat/tableau.py | 1 + src/sage/structure/sage_object.pyx | 2 +- src/sage/typeset/unicode_art.py | 6 +++--- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/shuffle.py b/src/sage/combinat/shuffle.py index 0dda028b349..f90b13f09f6 100644 --- a/src/sage/combinat/shuffle.py +++ b/src/sage/combinat/shuffle.py @@ -160,10 +160,10 @@ def _ascii_art_(self): [ [ ], [ o o ] ] and [ [ 1, 4 ] ] """ - from sage.misc.ascii_art import ascii_art, ascii_art_list + from sage.misc.ascii_art import ascii_art return ascii_art("Set shuffle product of:") * \ - (ascii_art_list(self._l1) + ascii_art(" and ") + - ascii_art_list(self._l2)) + (ascii_art(self._l1) + ascii_art(" and ") + + ascii_art(self._l2)) def __iter__(self): """ @@ -304,10 +304,10 @@ def _ascii_art_(self): [ / \ ] [ / \ / ] [ o o ] and [ o o o ] """ - from sage.misc.ascii_art import ascii_art, ascii_art_list + from sage.misc.ascii_art import ascii_art return ascii_art("Shuffle product of:") * \ - (ascii_art_list(self._l1) + ascii_art(" and ") + - ascii_art_list(self._l2)) + (ascii_art(self._l1) + ascii_art(" and ") + + ascii_art(self._l2)) def __iter__(self): r""" diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index 17956fbb902..b8bb91a610b 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Tableaux diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index 858f87982f2..e11b523e488 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -285,7 +285,7 @@ cdef class SageObject: TESTS:: - sage: 1._ascii_art_() + sage: 1._unicode_art_() 1 sage: type(_) diff --git a/src/sage/typeset/unicode_art.py b/src/sage/typeset/unicode_art.py index 872a4319983..7ef359bb8fd 100644 --- a/src/sage/typeset/unicode_art.py +++ b/src/sage/typeset/unicode_art.py @@ -114,9 +114,9 @@ def unicode_art(*obj, **kwds): __________ 2*x*\/ -4*x + 1 sage: unicode_art(list(DyckWords(3))) - ⎡ /\ ⎤ - ⎢ /\ /\ /\/\ / \ ⎥ - ⎣ /\/\/\, /\/ \, / \/\, / \, / \ ⎦ + ⎡ ╱╲ ⎤ + ⎢ ╱╲ ╱╲ ╱╲╱╲ ╱ ╲ ⎥ + ⎣ ╱╲╱╲╱╲, ╱╲╱ ╲, ╱ ╲╱╲, ╱ ╲, ╱ ╲ ⎦ sage: ascii_art(1) 1 """ From bdcf06b1f6b41874ad15726d1c9bf9eacacb646b Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sun, 17 May 2015 18:02:02 +0200 Subject: [PATCH 07/12] Workarounds to build PDF docs --- src/doc/common/conf.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/doc/common/conf.py b/src/doc/common/conf.py index 00b10dcf9c8..3a76b40d8ed 100644 --- a/src/doc/common/conf.py +++ b/src/doc/common/conf.py @@ -369,7 +369,7 @@ def add(subdoc=''): \DeclareUnicodeCharacter{2208}{\in} \DeclareUnicodeCharacter{2209}{\notin} \DeclareUnicodeCharacter{2202}{\partial} -\DeclareUnicodeCharacter{222B}{\int} +\DeclareUnicodeCharacter{222B}{\ensuremath{\int}} \DeclareUnicodeCharacter{2148}{\id} \DeclareUnicodeCharacter{2248}{\approx} \DeclareUnicodeCharacter{2260}{\neq} @@ -399,6 +399,22 @@ def add(subdoc=''): \DeclareUnicodeCharacter{23A5}{\sageMexSymbol{"37}} % bracketrightex \DeclareUnicodeCharacter{23A6}{\sageMexSymbol{"35}} % bracketrightbt +\DeclareUnicodeCharacter{23A7}{\sageMexSymbol{"38}} % curly brace left top +\DeclareUnicodeCharacter{23A8}{\sageMexSymbol{"3C}} % curly brace left middle +\DeclareUnicodeCharacter{23A9}{\sageMexSymbol{"3A}} % curly brace left bottom +\DeclareUnicodeCharacter{23AA}{\sageMexSymbol{"3E}} % curly brace extension +\DeclareUnicodeCharacter{23AB}{\sageMexSymbol{"39}} % curly brace right top +\DeclareUnicodeCharacter{23AC}{\sageMexSymbol{"3D}} % curly brace right middle +\DeclareUnicodeCharacter{23AD}{\sageMexSymbol{"3B}} % curly brace right bottom +\DeclareUnicodeCharacter{23B0}{\{} % 2-line curly brace left top half (not in cmex) +\DeclareUnicodeCharacter{23B1}{\}} % 2-line curly brace right top half (not in cmex) + +\DeclareUnicodeCharacter{2320}{\ensuremath{\int}} % top half integral +\DeclareUnicodeCharacter{2321}{\ensuremath{\int}} % bottom half integral +\DeclareUnicodeCharacter{23AE}{\ensuremath{\|}} % integral extenison + +\DeclareUnicodeCharacter{2571}{/} % Box drawings light diagonal upper right to lower left + \let\textLaTeX\LaTeX \renewcommand*{\LaTeX}{\hbox{\textLaTeX}} """ From 980c2f60198abcc9a2ea6b415d3849c2b24c12b2 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sun, 17 May 2015 18:02:22 +0200 Subject: [PATCH 08/12] Switch sage.misc.ascii_art -> sage.typeset.ascii_art --- src/doc/en/reference/misc/index.rst | 6 +++++- src/sage/combinat/abstract_tree.py | 4 ++-- src/sage/combinat/binary_tree.py | 4 ++-- src/sage/combinat/composition.py | 2 +- src/sage/combinat/crystals/tensor_product.py | 2 +- src/sage/combinat/dyck_word.py | 2 +- src/sage/combinat/free_module.py | 4 ++-- src/sage/combinat/partition.py | 2 +- src/sage/combinat/rigged_configurations/kr_tableaux.py | 2 +- .../rigged_configurations/rigged_configuration_element.py | 2 +- src/sage/combinat/shuffle.py | 4 ++-- src/sage/combinat/skew_partition.py | 2 +- src/sage/combinat/skew_tableau.py | 2 +- src/sage/combinat/tableau.py | 2 +- src/sage/combinat/tableau_tuple.py | 2 +- src/sage/geometry/polyhedron/double_description.py | 2 +- src/sage/homology/chain_complex.py | 4 ++-- src/sage/misc/ascii_art.py | 7 ------- src/sage/monoids/indexed_free_monoid.py | 2 +- src/sage/numerical/linear_tensor_constraints.py | 2 +- src/sage/repl/display/formatter.py | 2 +- src/sage/structure/indexed_generators.py | 2 +- src/sage/symbolic/expression.pyx | 4 ++-- src/sage/typeset/character_art_factory.py | 8 ++++---- 24 files changed, 36 insertions(+), 39 deletions(-) delete mode 100644 src/sage/misc/ascii_art.py diff --git a/src/doc/en/reference/misc/index.rst b/src/doc/en/reference/misc/index.rst index 30b6510aae7..a2f59ba9bc2 100644 --- a/src/doc/en/reference/misc/index.rst +++ b/src/doc/en/reference/misc/index.rst @@ -171,7 +171,11 @@ Formatted Output .. toctree:: :maxdepth: 1 - sage/misc/ascii_art + sage/typeset/symbols + sage/typeset/character_art + sage/typeset/character_art_factory + sage/typeset/ascii_art + sage/typeset/unicode_art sage/misc/sage_input sage/misc/table diff --git a/src/sage/combinat/abstract_tree.py b/src/sage/combinat/abstract_tree.py index 32e3728e072..be6a4414a2e 100644 --- a/src/sage/combinat/abstract_tree.py +++ b/src/sage/combinat/abstract_tree.py @@ -951,10 +951,10 @@ def _ascii_art_(self): node_to_str = lambda t: str(t.label()) if hasattr(t, "label") else "o" if self.is_empty(): - from sage.misc.ascii_art import empty_ascii_art + from sage.typeset.ascii_art import empty_ascii_art return empty_ascii_art - from sage.misc.ascii_art import AsciiArt + from sage.typeset.ascii_art import AsciiArt if len(self) == 0: t_repr = AsciiArt( [node_to_str(self)] ) t_repr._root = 1 diff --git a/src/sage/combinat/binary_tree.py b/src/sage/combinat/binary_tree.py index ffaae3cd65a..b5385adf664 100644 --- a/src/sage/combinat/binary_tree.py +++ b/src/sage/combinat/binary_tree.py @@ -376,10 +376,10 @@ def _ascii_art_( self ): node_to_str = lambda bt: str(bt.label()) if hasattr(bt, "label") else "o" if self.is_empty(): - from sage.misc.ascii_art import empty_ascii_art + from sage.typeset.ascii_art import empty_ascii_art return empty_ascii_art - from sage.misc.ascii_art import AsciiArt + from sage.typeset.ascii_art import AsciiArt if self[0].is_empty() and self[1].is_empty(): bt_repr = AsciiArt( [node_to_str(self)] ) bt_repr._root = 1 diff --git a/src/sage/combinat/composition.py b/src/sage/combinat/composition.py index 45297fa2df0..498c3b3fea7 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -160,7 +160,7 @@ def _ascii_art_(self): [ # # ## # # ## ### ] [ #, ##, #, ###, #, ##, #, #### ] """ - from sage.misc.ascii_art import ascii_art + from sage.typeset.ascii_art import ascii_art return ascii_art(self.to_skew_partition()) def __setstate__(self, state): diff --git a/src/sage/combinat/crystals/tensor_product.py b/src/sage/combinat/crystals/tensor_product.py index 769ef4fd245..82b99f97f4d 100644 --- a/src/sage/combinat/crystals/tensor_product.py +++ b/src/sage/combinat/crystals/tensor_product.py @@ -893,7 +893,7 @@ def _ascii_art_(self): 3 3 3 2 -4 -4 -4 """ - from sage.misc.ascii_art import ascii_art, AsciiArt + from sage.typeset.ascii_art import ascii_art, AsciiArt s = ascii_art(self[0]) s._baseline = s._h // 2 ret = s diff --git a/src/sage/combinat/dyck_word.py b/src/sage/combinat/dyck_word.py index 0f2e02f5605..89dbb93a654 100644 --- a/src/sage/combinat/dyck_word.py +++ b/src/sage/combinat/dyck_word.py @@ -606,7 +606,7 @@ def _ascii_art_(self): [ /\ /\ /\/\ / \ ] [ /\/\/\, /\/ \, / \/\, / \, / \ ] """ - from sage.misc.ascii_art import AsciiArt + from sage.typeset.ascii_art import AsciiArt rep = self.parent().global_options['ascii_art'] if rep == "path": ret = self.to_path_string() diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index acf36103ba6..7e775dfcd40 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -29,7 +29,7 @@ from sage.categories.all import Category, Sets, ModulesWithBasis from sage.combinat.dict_addition import dict_addition, dict_linear_combination from sage.sets.family import Family -from sage.misc.ascii_art import AsciiArt, empty_ascii_art +from sage.typeset.ascii_art import AsciiArt, empty_ascii_art # TODO: move the content of this class to CombinatorialFreeModule.Element and ModulesWithBasis.Element class CombinatorialFreeModuleElement(Element): @@ -1328,7 +1328,7 @@ def _ascii_art_term(self, m): sage: ascii_art(R.one()) # indirect doctest 1 """ - from sage.misc.ascii_art import AsciiArt + from sage.typeset.ascii_art import AsciiArt try: if m == self.one_basis(): return AsciiArt(["1"]) diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index dc77deb712b..efc3efb8f5e 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -729,7 +729,7 @@ def _ascii_art_(self): [ **** *** * ** * * ] [ *****, * , ** , * , * , * , * ] """ - from sage.misc.ascii_art import AsciiArt + from sage.typeset.ascii_art import AsciiArt return AsciiArt(self._repr_diagram().splitlines(), baseline=0) def _repr_list(self): diff --git a/src/sage/combinat/rigged_configurations/kr_tableaux.py b/src/sage/combinat/rigged_configurations/kr_tableaux.py index 76cc6c7e4b0..c6962541a98 100644 --- a/src/sage/combinat/rigged_configurations/kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/kr_tableaux.py @@ -1083,7 +1083,7 @@ def _ascii_art_(self): 1 3 2 4 """ - from sage.misc.ascii_art import AsciiArt + from sage.typeset.ascii_art import AsciiArt return AsciiArt(self._repr_diagram().splitlines()) def to_kirillov_reshetikhin_crystal(self): diff --git a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py index 47b558bf4b4..4ab2cb1b8d6 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py +++ b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py @@ -448,7 +448,7 @@ def _ascii_art_(self): baseline = lambda s: 0 else: baseline = lambda s: len(s) - from sage.misc.ascii_art import AsciiArt + from sage.typeset.ascii_art import AsciiArt s = repr(self[0]).splitlines() ret = AsciiArt(s, baseline=baseline(s)) for tableau in self[1:]: diff --git a/src/sage/combinat/shuffle.py b/src/sage/combinat/shuffle.py index f90b13f09f6..fd29264bd00 100644 --- a/src/sage/combinat/shuffle.py +++ b/src/sage/combinat/shuffle.py @@ -160,7 +160,7 @@ def _ascii_art_(self): [ [ ], [ o o ] ] and [ [ 1, 4 ] ] """ - from sage.misc.ascii_art import ascii_art + from sage.typeset.ascii_art import ascii_art return ascii_art("Set shuffle product of:") * \ (ascii_art(self._l1) + ascii_art(" and ") + ascii_art(self._l2)) @@ -304,7 +304,7 @@ def _ascii_art_(self): [ / \ ] [ / \ / ] [ o o ] and [ o o o ] """ - from sage.misc.ascii_art import ascii_art + from sage.typeset.ascii_art import ascii_art return ascii_art("Shuffle product of:") * \ (ascii_art(self._l1) + ascii_art(" and ") + ascii_art(self._l2)) diff --git a/src/sage/combinat/skew_partition.py b/src/sage/combinat/skew_partition.py index 7328a423493..0116d0b8001 100644 --- a/src/sage/combinat/skew_partition.py +++ b/src/sage/combinat/skew_partition.py @@ -500,7 +500,7 @@ def _ascii_art_(self): [ ###, ##, ##, #, #, #, #, #, # ] sage: SkewPartitions.global_options.reset() """ - from sage.misc.ascii_art import AsciiArt + from sage.typeset.ascii_art import AsciiArt return AsciiArt(self.diagram().splitlines()) def inner(self): diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index 19ad9c98f3c..b5b53603396 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -289,7 +289,7 @@ def _ascii_art_(self): [ 1 3 1 2 ] [ 2 , 3 ] """ - from sage.misc.ascii_art import AsciiArt + from sage.typeset.ascii_art import AsciiArt return AsciiArt(self._repr_diagram().splitlines()) def _latex_(self): diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index b8bb91a610b..ea115e65a30 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -517,7 +517,7 @@ def _ascii_art_(self): sage: Tableaux.global_options.reset() """ ascii = self.parent().global_options.dispatch(self,'_ascii_art_','ascii_art') - from sage.misc.ascii_art import AsciiArt + from sage.typeset.ascii_art import AsciiArt return AsciiArt(ascii.splitlines()) def _unicode_art_(self): diff --git a/src/sage/combinat/tableau_tuple.py b/src/sage/combinat/tableau_tuple.py index 0fe41f326b8..0bfd735f71e 100644 --- a/src/sage/combinat/tableau_tuple.py +++ b/src/sage/combinat/tableau_tuple.py @@ -502,7 +502,7 @@ def _ascii_art_(self): 2 3 - 4 - 5 """ - from sage.misc.ascii_art import AsciiArt + from sage.typeset.ascii_art import AsciiArt return AsciiArt(self._repr_diagram().splitlines()) def _latex_(self): diff --git a/src/sage/geometry/polyhedron/double_description.py b/src/sage/geometry/polyhedron/double_description.py index bb608143100..3bff500b362 100644 --- a/src/sage/geometry/polyhedron/double_description.py +++ b/src/sage/geometry/polyhedron/double_description.py @@ -213,7 +213,7 @@ def __repr__(self): [ 2/3 -1/3 -1/3]\nA = [ 0 1 1], R = [-1/3 2/3 -1/3]\n [-1 -1 1] [ 1/3 1/3 1/3]' """ - from sage.misc.ascii_art import ascii_art + from sage.typeset.ascii_art import ascii_art from sage.matrix.constructor import matrix s = ascii_art('Double description pair (A, R) defined by') A = ascii_art(matrix(self.A)) diff --git a/src/sage/homology/chain_complex.py b/src/sage/homology/chain_complex.py index 2c56a290fdc..9ea0e214983 100644 --- a/src/sage/homology/chain_complex.py +++ b/src/sage/homology/chain_complex.py @@ -416,7 +416,7 @@ def _ascii_art_(self): 0 <---- [0] <---- [4] <---- [2] <----- 0 [5] [3] """ - from sage.misc.ascii_art import AsciiArt + from sage.typeset.ascii_art import AsciiArt def arrow_art(d): d_str = [' d_{0} '.format(d)] @@ -1581,7 +1581,7 @@ def _ascii_art_(self): [1] [1] [0] [1] 0 <-- C_7 <---- C_6 <-- 0 ... 0 <-- C_3 <---- C_2 <---- C_1 <---- C_0 <-- 0 """ - from sage.misc.ascii_art import AsciiArt + from sage.typeset.ascii_art import AsciiArt def arrow_art(n): d_n = self.differential(n) diff --git a/src/sage/misc/ascii_art.py b/src/sage/misc/ascii_art.py deleted file mode 100644 index ba80f1c177f..00000000000 --- a/src/sage/misc/ascii_art.py +++ /dev/null @@ -1,7 +0,0 @@ -""" -Temporary redirecton - -See :trac:`18357`. -""" - -from sage.typeset.ascii_art import AsciiArt, ascii_art, empty_ascii_art diff --git a/src/sage/monoids/indexed_free_monoid.py b/src/sage/monoids/indexed_free_monoid.py index 7d3ec7b9e7e..5fa05b0de25 100644 --- a/src/sage/monoids/indexed_free_monoid.py +++ b/src/sage/monoids/indexed_free_monoid.py @@ -128,7 +128,7 @@ def _ascii_art_(self): F *F *F *F 0 1 3 4 """ - from sage.misc.ascii_art import AsciiArt, ascii_art, empty_ascii_art + from sage.typeset.ascii_art import AsciiArt, ascii_art, empty_ascii_art if not self._monomial: return AsciiArt(["1"]) diff --git a/src/sage/numerical/linear_tensor_constraints.py b/src/sage/numerical/linear_tensor_constraints.py index 177a3abbc13..24d9daa922a 100644 --- a/src/sage/numerical/linear_tensor_constraints.py +++ b/src/sage/numerical/linear_tensor_constraints.py @@ -255,7 +255,7 @@ def _ascii_art_(self): [0 0] <= [x_0 2*x_0] [0 0] [3*x_0 4*x_0] """ - from sage.misc.ascii_art import AsciiArt + from sage.typeset.ascii_art import AsciiArt def matrix_art(m): lines = str(m).splitlines() return AsciiArt(lines, baseline=len(lines)/2) diff --git a/src/sage/repl/display/formatter.py b/src/sage/repl/display/formatter.py index 4e3e5ecab04..39776cddab1 100644 --- a/src/sage/repl/display/formatter.py +++ b/src/sage/repl/display/formatter.py @@ -48,7 +48,7 @@ sage: shell.quit() This other facility uses a simple -:class:`~sage.misc.ascii_art.AsciiArt` object (see and +:class:`~sage.typeset.ascii_art.AsciiArt` object (see and :meth:`sage.structure.sage_object.SageObject._ascii_art_`). """ #***************************************************************************** diff --git a/src/sage/structure/indexed_generators.py b/src/sage/structure/indexed_generators.py index becf9d4ae2e..2f445492466 100644 --- a/src/sage/structure/indexed_generators.py +++ b/src/sage/structure/indexed_generators.py @@ -332,7 +332,7 @@ def _ascii_art_generator(self, m): ## #### """ - from sage.misc.ascii_art import AsciiArt, ascii_art + from sage.typeset.ascii_art import AsciiArt, ascii_art pref = AsciiArt([self.prefix()]) r = pref * (AsciiArt([" "**Integer(len(pref))]) + ascii_art(m)) r._baseline = r._h - 1 diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index ac9cb2776a3..6078a963d11 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -544,10 +544,10 @@ cdef class Expression(CommutativeRingElement): / """ from sympy import pretty, sympify - from sage.misc.ascii_art import AsciiArt + from sage.typeset.ascii_art import AsciiArt # FIXME:: when *sage* will use at least sympy >= 0.7.2 # we could use a nice splitting with respect of the AsciiArt module. - # from sage.misc.ascii_art import AsciiArt, MAX_LENGTH ## for import + # from sage.typeset.ascii_art import AsciiArt, MAX_LENGTH ## for import # num_columns = MAX_LENGTH ## option of pretty try: s = pretty(sympify(self, evaluate=False), use_unicode=False) diff --git a/src/sage/typeset/character_art_factory.py b/src/sage/typeset/character_art_factory.py index dd7b1de8b2c..447332ee79e 100644 --- a/src/sage/typeset/character_art_factory.py +++ b/src/sage/typeset/character_art_factory.py @@ -26,16 +26,16 @@ class CharacterArtFactory(SageObject): def __init__(self, art_type, string_type, magic_method_name, parenthesis, square_bracet, curly_brace): - """ + r""" Abstract base class for character art factory This class is the common implementation behind :func:`~sage.typeset.ascii_art.ascii_art` and - :func:`sage.typeset.unicode_art.unicode_art`. + :func:`~sage.typeset.unicode_art.unicode_art` . INPUT: - - ``art_type` -- type of the character art. + - ``art_type`` -- type of the character art. - ``string_type`` -- type of strings (the lines in the character art). @@ -155,7 +155,7 @@ def build_from_magic_method(self, obj): return magic_method() def build_from_string(self, obj): - """ + r""" Return the character art object created from splitting the object's string representation INPUT: From 03a98546bb4945bb5a5da59c1d1f39d6bdb9839b Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 20 May 2015 19:51:32 +0200 Subject: [PATCH 09/12] Trac 18357: remove trailing whitespaces --- src/sage/matrix/matrix0.pyx | 8 ++-- src/sage/repl/rich_output/backend_base.py | 28 ++++++------ src/sage/repl/rich_output/buffer.py | 12 ++--- src/sage/repl/rich_output/output_basic.py | 30 ++++++------- src/sage/typeset/ascii_art.py | 6 +-- src/sage/typeset/character_art.py | 22 ++++----- src/sage/typeset/character_art_factory.py | 54 +++++++++++------------ src/sage/typeset/symbols.py | 12 ++--- src/sage/typeset/unicode_art.py | 4 +- 9 files changed, 88 insertions(+), 88 deletions(-) diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index 177ab06ec5c..836a0be8810 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -1814,23 +1814,23 @@ cdef class Matrix(sage.structure.element.Matrix): if shape == "square": if unicode: from sage.typeset.symbols import ( - unicode_left_square_bracket as left, + unicode_left_square_bracket as left, unicode_right_square_bracket as right ) else: from sage.typeset.symbols import ( - ascii_left_square_bracket as left, + ascii_left_square_bracket as left, ascii_right_square_bracket as right ) elif shape == "round": if unicode: from sage.typeset.symbols import ( - unicode_left_parenthesis as left, + unicode_left_parenthesis as left, unicode_right_parenthesis as right ) else: from sage.typeset.symbols import ( - ascii_left_parenthesis as left, + ascii_left_parenthesis as left, ascii_right_parenthesis as right ) else: diff --git a/src/sage/repl/rich_output/backend_base.py b/src/sage/repl/rich_output/backend_base.py index 7b5a4758585..25b5350ba08 100644 --- a/src/sage/repl/rich_output/backend_base.py +++ b/src/sage/repl/rich_output/backend_base.py @@ -88,7 +88,7 @@ def get_display_manager(self): instance. EXAMPLES:: - + sage: from sage.repl.rich_output.backend_base import BackendBase sage: backend = BackendBase() sage: backend.get_display_manager() @@ -234,7 +234,7 @@ def _apply_pretty_printer(self, pretty_printer_class, obj): OUTPUT: String. - + EXAMPLES:: sage: from sage.repl.rich_output.backend_base import BackendBase @@ -288,9 +288,9 @@ def plain_text_formatter(self, obj, **kwds): sage: out.text buffer containing 139 bytes sage: out.text.get() - '[0,\n 1,\n 2,\n 3,\n 4,\n 5,\n 6,\n 7,\n 8,\n 9,\n - 10,\n 11,\n 12,\n 13,\n 14,\n 15,\n 16,\n 17,\n 18,\n - 19,\n 20,\n 21,\n 22,\n 23,\n 24,\n 25,\n 26,\n 27,\n + '[0,\n 1,\n 2,\n 3,\n 4,\n 5,\n 6,\n 7,\n 8,\n 9,\n + 10,\n 11,\n 12,\n 13,\n 14,\n 15,\n 16,\n 17,\n 18,\n + 19,\n 20,\n 21,\n 22,\n 23,\n 24,\n 25,\n 26,\n 27,\n 28,\n 29]' sage: out = backend.plain_text_formatter(range(20), concatenate=True) @@ -338,7 +338,7 @@ def ascii_art_formatter(self, obj, **kwds): sage: out.ascii_art buffer containing 228 bytes sage: print(out.ascii_art.get()) - [ + [ [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, ] @@ -389,7 +389,7 @@ def unicode_art_formatter(self, obj, **kwds): sage: out.unicode_art buffer containing 236 bytes sage: print(out.unicode_art.get()) - ⎡ + ⎡ ⎣ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, ⎤ @@ -407,7 +407,7 @@ def unicode_art_formatter(self, obj, **kwds): result = unicode_art(obj) from sage.repl.rich_output.output_basic import OutputUnicodeArt return OutputUnicodeArt(str(result)) - + def latex_formatter(self, obj, **kwds): r""" Hook to override how Latex is being formatted. @@ -495,11 +495,11 @@ def set_underscore_variable(self, obj): """ import __builtin__ __builtin__._ = obj - + def displayhook(self, plain_text, rich_output): """ Backend implementation of the displayhook - + The value of the last statement on a REPL input line or notebook cell are usually handed to the Python displayhook and shown on screen. By overriding this method you define how @@ -508,7 +508,7 @@ def displayhook(self, plain_text, rich_output): most suitable rich output container. Derived classes must implement this method. - + INPUT: - ``plain_text`` -- instance of @@ -551,7 +551,7 @@ def display_immediately(self, plain_text, rich_output): up being called by :meth:`sage.plot.graphics.Graphics.show`. Derived classes must implement this method. - + INPUT: Same as :meth:`displayhook`. @@ -583,14 +583,14 @@ class BackendSimple(BackendBase): Simple Backend This backend only supports plain text. - + EXAMPLES:: sage: from sage.repl.rich_output.backend_base import BackendSimple sage: BackendSimple() simple """ - + def _repr_(self): r""" Return string representation of the backend diff --git a/src/sage/repl/rich_output/buffer.py b/src/sage/repl/rich_output/buffer.py index 75c2d8c24eb..e5903486836 100644 --- a/src/sage/repl/rich_output/buffer.py +++ b/src/sage/repl/rich_output/buffer.py @@ -87,7 +87,7 @@ def from_file(cls, filename): stored. OUTPUT: - + String containing the buffer data. EXAMPLES:: @@ -141,7 +141,7 @@ def _chmod_readonly(cls, filename): mode = os.stat(filename).st_mode mode = stat.S_IMODE(mode) & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH) os.chmod(filename, mode) - + def _repr_(self): """ Return a string representation @@ -157,11 +157,11 @@ def _repr_(self): buffer containing 8 bytes """ return 'buffer containing {0} bytes'.format(len(self.get())) - + def get(self): """ Return the buffer content - + OUTPUT: Bytes. A string in Python 2.x. @@ -180,7 +180,7 @@ def get(self): def get_unicode(self): """ Return the buffer content as string - + OUTPUT: String. Unicode in Python 2.x. Raises a ``UnicodeEncodeError`` @@ -251,7 +251,7 @@ def filename(self, ext=None): self._chmod_readonly(output) return output - + def save_as(self, filename): """ Save a copy of the buffer content. diff --git a/src/sage/repl/rich_output/output_basic.py b/src/sage/repl/rich_output/output_basic.py index 656b1c08a05..c298b895896 100644 --- a/src/sage/repl/rich_output/output_basic.py +++ b/src/sage/repl/rich_output/output_basic.py @@ -8,7 +8,7 @@ :class:`OutputPlainText`. Some output classes consist of more than one data buffer, for example jmol or certain animation formats. The output class is independent of user preferences and of the display -backend. +backend. The display backends can define derived classes to attach backend-specific display functionality to, for example how to launch a @@ -29,7 +29,7 @@ class is independent of user preferences and of the display instances. You must never reference any files on the local file system, as there is no guarantee that the notebook server and the worker process are on the same computer. Or even share a common - file system. + file system. """ #***************************************************************************** @@ -79,7 +79,7 @@ def example(cls): OUTPUT: An instance of the :class:`OutputBase` subclass. - + EXAMPLES:: sage: from sage.repl.rich_output.output_basic import OutputBase @@ -96,7 +96,7 @@ class OutputPlainText(OutputBase): def __init__(self, plain_text): """ Plain Text Output - + INPUT: - ``plain_text`` -- @@ -134,7 +134,7 @@ def example(cls): OUTPUT: An instance of :class:`OutputPlainText`. - + EXAMPLES:: sage: from sage.repl.rich_output.output_catalog import OutputPlainText @@ -166,7 +166,7 @@ class OutputAsciiArt(OutputBase): def __init__(self, ascii_art): """ ASCII Art Output - + INPUT: - ``ascii_art`` -- @@ -182,7 +182,7 @@ def __init__(self, ascii_art): sage: OutputAsciiArt(':-}') OutputAsciiArt container """ - self.ascii_art = OutputBuffer(ascii_art) + self.ascii_art = OutputBuffer(ascii_art) @classmethod def example(cls): @@ -195,7 +195,7 @@ def example(cls): OUTPUT: An instance of :class:`OutputAsciiArt`. - + EXAMPLES:: sage: from sage.repl.rich_output.output_catalog import OutputAsciiArt @@ -231,7 +231,7 @@ class OutputUnicodeArt(OutputBase): def __init__(self, unicode_art): """ Unicode Art Output - + Similar to :class:`OutputAsciiArt` but using the entire unicode range. @@ -267,7 +267,7 @@ def example(cls): OUTPUT: An instance of :class:`OutputUnicodeArt`. - + EXAMPLES:: sage: from sage.repl.rich_output.output_catalog import OutputUnicodeArt @@ -305,10 +305,10 @@ def __init__(self, latex): LaTeX Output .. note:: - + The LaTeX commands will only use a subset of LaTeX that can be displayed by MathJax. - + INPUT: - ``latex`` -- @@ -363,7 +363,7 @@ def display_equation(self): OUTPUT: String. - + EXAMPLES:: sage: from sage.repl.rich_output.output_catalog import OutputLatex @@ -384,7 +384,7 @@ def inline_equation(self): OUTPUT: String. - + EXAMPLES:: sage: from sage.repl.rich_output.output_catalog import OutputLatex @@ -409,7 +409,7 @@ def example(cls): OUTPUT: An instance of :class:`OutputLatex`. - + EXAMPLES:: sage: from sage.repl.rich_output.output_catalog import OutputLatex diff --git a/src/sage/typeset/ascii_art.py b/src/sage/typeset/ascii_art.py index 298b8273d86..7e49a4ce97e 100644 --- a/src/sage/typeset/ascii_art.py +++ b/src/sage/typeset/ascii_art.py @@ -182,7 +182,7 @@ class AsciiArt(CharacterArt): e """ _string_type = str - + _ascii_art_factory = CharacterArtFactory( @@ -211,7 +211,7 @@ def ascii_art(*obj, **kwds): more than one object given. OUTPUT: - + :class:`AsciiArt` instance. EXAMPLES:: @@ -232,7 +232,7 @@ def ascii_art(*obj, **kwds): [1 0 0] [1 0] [0 1 0] [1] : [0 1] : [0 0 1] - + TESTS:: sage: n = var('n') diff --git a/src/sage/typeset/character_art.py b/src/sage/typeset/character_art.py index 101d303d6a9..9af5be6b137 100644 --- a/src/sage/typeset/character_art.py +++ b/src/sage/typeset/character_art.py @@ -41,27 +41,27 @@ class CharacterArt(SageObject): def __init__(self, lines=[], breakpoints=[], baseline=None, atomic=True): r""" Abstract base class for character art - + INPUT: - + - ``lines`` -- the list of lines of the representation of the character art object - + - ``breakpoints`` -- the list of points where the representation can be split - + - ``baseline`` -- the reference line (from the bottom) - + - ``atomic`` -- indicate if the character art representation is splittable (must be coherent with breakpoints) - + EXAMPLES:: - + sage: i = var('i') sage: ascii_art(sum(pi^i/factorial(i)*x^i, i, 0, oo)) pi*x e - + TESTS:: sage: from sage.typeset.ascii_art import AsciiArt @@ -96,7 +96,7 @@ def empty(cls): """ empty_string = cls._string_type() return cls([empty_string]) - + def __getitem__(self, key): r""" Return the line `key` of the ASCII art object. @@ -267,7 +267,7 @@ def _isatty(self): # The IPython zeromq kernel uses a fake stdout that does # not support fileno() return False - + def _terminal_width(self): """ Compute the width size of the terminal. @@ -536,7 +536,7 @@ def height(self): 3 """ return self._h - + def __add__(self, Nelt): r""" Concatenate two ascii art object. diff --git a/src/sage/typeset/character_art_factory.py b/src/sage/typeset/character_art_factory.py index 447332ee79e..beeb76c6d4b 100644 --- a/src/sage/typeset/character_art_factory.py +++ b/src/sage/typeset/character_art_factory.py @@ -65,7 +65,7 @@ def __init__(self, self.left_parenthesis, self.right_parenthesis = parenthesis self.left_square_bracket, self.right_square_bracket = square_bracet self.left_curly_brace, self.right_curly_brace = curly_brace - + def build(self, obj): r""" Construct a character art reprensentation @@ -76,11 +76,11 @@ def build(self, obj): we want. OUTPUT: - + Character art object. EXAMPLES:: - + sage: ascii_art(integral(exp(x+x^2)/(x+1), x)) / | @@ -91,9 +91,9 @@ def build(self, obj): | x + 1 | / - + TESTS:: - + sage: n = var('n') sage: ascii_art(sum(binomial(2 * n, n + 1) * x^n, n, 0, oo)) / __________ \ @@ -123,7 +123,7 @@ def build(self, obj): return self.build_from_magic_method(obj) else: return self.build_from_string(obj) - + def build_empty(self): """ Return the empty character art object @@ -189,20 +189,20 @@ def build_from_string(self, obj): if self.string_type is str and not isinstance(obj, str): obj = unicode(obj).encode('utf-8') return self.art_type(obj.splitlines()) - + def build_comma_sequence(self, iterable, func=None): r""" Build up a comma-separated sequence Auxiliary function for ``build_X`` where ``X`` is ``dict``, ``set``, ``list``, or ``tuple``. - + EXAMPLES:: - + sage: from sage.typeset.ascii_art import _ascii_art_factory as factory sage: out = factory.build_comma_sequence(list(DyckWords(3))); out - /\ - /\ /\ /\/\ / \ + /\ + /\ /\ /\/\ / \ /\/\/\, /\/ \, / \/\, / \, / \ sage: type(out) @@ -228,13 +228,13 @@ def build_comma_sequence(self, iterable, func=None): repr_elems += func(elem, iterable) not_first = True return repr_elems - + def build_set(self, set): r""" Return an character art output of a set. - + TESTS:: - + sage: ascii_art(set(DyckWords(3))) { /\ } { /\ /\/\ /\ / \ } @@ -243,13 +243,13 @@ def build_set(self, set): repr_elems = self.build_comma_sequence(set) return self.build_container( repr_elems, self.left_curly_brace, self.right_curly_brace) - + def build_dict(self, dict): r""" Return an character art output of a dictionnary. - + TESTS:: - + sage: ascii_art({i:dw for i,dw in enumerate(DyckWords(3))}) { /\ } { /\ /\ /\/\ / \ } @@ -261,11 +261,11 @@ def func(k, dict): repr_elems = self.build_comma_sequence(dict, func) return self.build_container( repr_elems, self.left_curly_brace, self.right_curly_brace) - + def build_container(self, content, left_border, right_border): r""" Return character art for a container - + INPUT: - ``content`` -- @@ -281,7 +281,7 @@ def build_container(self, content, left_border, right_border): border of the container. TESTS:: - + sage: l = ascii_art(list(DyckWords(3))); l [ /\ ] [ /\ /\ /\/\ / \ ] @@ -303,13 +303,13 @@ def build_container(self, content, left_border, right_border): shift = len(left_border) + len(pad) basepoints = [bp + shift for bp in content.get_breakpoints()] + [w+shift] return self.art_type(lines, basepoints, baseline=0, atomic=False) - + def build_list(self, list): r""" Return an character art output of a list. - + TESTS:: - + sage: l = ascii_art(list(DyckWords(3))); l [ /\ ] [ /\ /\ /\/\ / \ ] @@ -320,13 +320,13 @@ def build_list(self, list): repr_elems = self.build_comma_sequence(list) return self.build_container( repr_elems, self.left_square_bracket, self.right_square_bracket) - + def build_tuple(self, tuple): r""" Return an character art output of a tuple. - + TESTS:: - + sage: ascii_art(tuple(DyckWords(3))) ( /\ ) ( /\ /\ /\/\ / \ ) @@ -335,7 +335,7 @@ def build_tuple(self, tuple): repr_elems = self.build_comma_sequence(tuple) return self.build_container( repr_elems, self.left_parenthesis, self.right_parenthesis) - + def concatenate(self, iterable, separator, empty): """ Concatenate multiple character art instances diff --git a/src/sage/typeset/symbols.py b/src/sage/typeset/symbols.py index 9ec5a540cea..2edba1fb7d0 100644 --- a/src/sage/typeset/symbols.py +++ b/src/sage/typeset/symbols.py @@ -28,7 +28,7 @@ ( ) [ ] { } ( ) ( ) [ ] [ ] { } { } ( ) ( ) ( ) [ ] [ ] [ ] { } { } { } - ( ) ( ) ( ) ( ) [ ] [ ] [ ] [ ] { } { } { } { } + ( ) ( ) ( ) ( ) [ ] [ ] [ ] [ ] { } { } { } { } sage: symbols = unicode_art(u'') sage: for i in range(1, 5): @@ -50,7 +50,7 @@ ⎛ ⎞ ⎡ ⎤ ⎧ ⎫ ⎛ ⎞ ⎜ ⎟ ⎡ ⎤ ⎢ ⎥ ⎧ ⎫ ⎭ ⎩ ⎛ ⎞ ⎜ ⎟ ⎜ ⎟ ⎡ ⎤ ⎢ ⎥ ⎢ ⎥ ⎰ ⎱ ⎨ ⎬ ⎫ ⎧ - ( ) ⎝ ⎠ ⎝ ⎠ ⎝ ⎠ [ ] ⎣ ⎦ ⎣ ⎦ ⎣ ⎦ { } ⎱ ⎰ ⎩ ⎭ ⎩ ⎭ + ( ) ⎝ ⎠ ⎝ ⎠ ⎝ ⎠ [ ] ⎣ ⎦ ⎣ ⎦ ⎣ ⎦ { } ⎱ ⎰ ⎩ ⎭ ⎩ ⎭ """ import unicodedata @@ -160,7 +160,7 @@ def __call__(self, num_lines): else: # num_lines %2 == 1 ext = [self.extension] * ((num_lines-3) // 2) return [self.top] + ext + [self.middle] + ext + [self.bottom] - + def print_to_stdout(self, num_lines): """ Print the multi-line symbol @@ -172,7 +172,7 @@ def print_to_stdout(self, num_lines): - ``num_lines`` -- integer. The total number of lines. EXAMPLES:: - + sage: from sage.typeset.symbols import * sage: unicode_integral.print_to_stdout(1) ∫ @@ -209,9 +209,9 @@ def character_art(self, num_lines): from sage.typeset.ascii_art import AsciiArt return AsciiArt(self(num_lines)) - + class CompoundUnicodeSymbol(CompoundSymbol): - + def character_art(self, num_lines): """ Return the unicode art of the symbol diff --git a/src/sage/typeset/unicode_art.py b/src/sage/typeset/unicode_art.py index 7ef359bb8fd..3e85815c482 100644 --- a/src/sage/typeset/unicode_art.py +++ b/src/sage/typeset/unicode_art.py @@ -82,7 +82,7 @@ def unicode_art(*obj, **kwds): more than one object given. OUTPUT: - + :class:`UnicodeArt` instance. EXAMPLES:: @@ -103,7 +103,7 @@ def unicode_art(*obj, **kwds): ⎛1 0 0⎞ ⎛1 0⎞ ⎜0 1 0⎟ (1) : ⎝0 1⎠ : ⎝0 0 1⎠ - + TESTS:: sage: n = var('n') From 161c0daca2f4a4752725225dfb9238ffc9692ddf Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Wed, 20 May 2015 21:18:47 +0200 Subject: [PATCH 10/12] Trac 18357: cleanup in character art --- src/sage/typeset/ascii_art.py | 3 -- src/sage/typeset/character_art.py | 61 +++++------------------ src/sage/typeset/character_art_factory.py | 6 +-- src/sage/typeset/unicode_art.py | 3 -- 4 files changed, 15 insertions(+), 58 deletions(-) diff --git a/src/sage/typeset/ascii_art.py b/src/sage/typeset/ascii_art.py index 7e49a4ce97e..05db7103389 100644 --- a/src/sage/typeset/ascii_art.py +++ b/src/sage/typeset/ascii_art.py @@ -171,9 +171,6 @@ class AsciiArt(CharacterArt): - ``baseline`` -- the reference line (from the bottom) - - ``atomic`` -- indicate if the ascii art representation is splittable - (must be coherent with breakpoints) - EXAMPLES:: sage: i = var('i') diff --git a/src/sage/typeset/character_art.py b/src/sage/typeset/character_art.py index 9af5be6b137..1ab1effe537 100644 --- a/src/sage/typeset/character_art.py +++ b/src/sage/typeset/character_art.py @@ -8,7 +8,7 @@ graphics by placing characters on a rectangular grid, in other words, using monospace fonts. The difference is that one is restricted to 7-bit ascii, the other uses all unicode code points. - """ +""" #******************************************************************************* # Copyright (C) 2013 Jean-Baptiste Priez , @@ -27,7 +27,6 @@ import os, sys from sage.structure.sage_object import SageObject -from sage.rings.integer import Integer ################################################################################ @@ -38,7 +37,7 @@ class CharacterArt(SageObject): - def __init__(self, lines=[], breakpoints=[], baseline=None, atomic=True): + def __init__(self, lines=[], breakpoints=[], baseline=None, atomic=None): r""" Abstract base class for character art @@ -52,9 +51,6 @@ def __init__(self, lines=[], breakpoints=[], baseline=None, atomic=True): - ``baseline`` -- the reference line (from the bottom) - - ``atomic`` -- indicate if the character art representation is - splittable (must be coherent with breakpoints) - EXAMPLES:: sage: i = var('i') @@ -66,8 +62,6 @@ def __init__(self, lines=[], breakpoints=[], baseline=None, atomic=True): sage: from sage.typeset.ascii_art import AsciiArt sage: aao = AsciiArt() - sage: aao.is_atomic() - True sage: aao sage: aa = AsciiArt([" * ", " * * ", "*****"]); aa @@ -75,14 +69,15 @@ def __init__(self, lines=[], breakpoints=[], baseline=None, atomic=True): * * ***** """ - self._is_uniq = True + if atomic is not None: + from sage.misc.superseded import deprecation + deprecation(18357, "the argument atomic is deprecated and will be ignored") self._matrix = lines self._breakpoints = breakpoints self._baseline = baseline if baseline is not None else 0 - self._is_atomic = atomic self._h = len(lines) - self._l = max(map(lambda line: len(line), lines) + [0]) + self._l = 0 if not lines else max([len(line) for line in lines]) @classmethod def empty(cls): @@ -144,7 +139,7 @@ def _repr_(self): hsize = self._terminal_width() ######### # if the draw is larger than the max length it try to split... - if hsize <= self._l and len(self._breakpoints) > 0: + if hsize <= self._l and self._breakpoints: return self._split_repr_(hsize) ######### output = "" @@ -154,36 +149,6 @@ def _repr_(self): return output + self._matrix[len(self._matrix) - 1] return output - def is_atomic(self): - r""" - Return ``True`` if the object is not splitable - - For example, we considere a linear expression:: - - sage: a = 14*x^5 + 5*x^4 - sage: ascii_art(a) - 5 4 - 14*x + 5*x - - If ASCII art object is not atomic, it is splittable on the ``+`` - (in fact it is not really true because we use ``sympy`` to make - ASCII art). - - TESTS:: - - sage: from sage.typeset.ascii_art import AsciiArt - sage: aa = AsciiArt([" * ", " * * ", "*****"]); aa - * - * * - ***** - sage: aa.is_atomic() - True - sage: laa = ascii_art([aa,aa]) - sage: laa.is_atomic() - False - """ - return self._is_atomic - def get_baseline(self): r""" Return the line where the baseline is, for example:: @@ -305,7 +270,6 @@ def _split_repr_(self, size): * * ] , ***** ] """ - import sys f_split = self._breakpoints[0]; i = 1 while i < len(self._breakpoints) and self._breakpoints[i] < size: f_split = self._breakpoints[i] @@ -638,7 +602,7 @@ def __add__(self, Nelt): if self._baseline is not None and Nelt._baseline is not None: # left treatement for line in self._matrix: - new_matrix.append(line + " "** Integer(self._l - len(line))) + new_matrix.append(line + " " * (self._l - len(line))) if new_h > self._h: # | new_h > self._h @@ -648,7 +612,7 @@ def __add__(self, Nelt): # | } if new_baseline > self._baseline: for k in range(new_baseline - self._baseline): - new_matrix.append(" " ** Integer(self._l)) + new_matrix.append(" " * self._l) # | } new_h > self._h # | } new_h - new_baseline > self._h - self._baseline # ||<-- baseline number of white lines at the top @@ -658,7 +622,7 @@ def __add__(self, Nelt): # | if new_h - new_baseline > self._h - self._baseline: for _ in range((new_h - new_baseline) - (self._h - self._baseline)): - new_matrix.insert(0, " " ** Integer(self._l)) + new_matrix.insert(0, " " * self._l) # right treatement i = 0 @@ -675,10 +639,10 @@ def __add__(self, Nelt): new_matrix[i+j] += Nelt._matrix[j] else: for line in self._matrix: - new_matrix.append(line + " " ** Integer(self._l - len(line))) + new_matrix.append(line + " " * (self._l - len(line))) for i, line_i in enumerate(Nelt._matrix): if i == len(new_matrix): - new_matrix.append(" "**Integer(self._l) + line_i) + new_matrix.append(" " * self._l + line_i) else: new_matrix[i] += line_i # breakpoint @@ -691,7 +655,6 @@ def __add__(self, Nelt): lines=new_matrix, breakpoints=uniq(new_breakpoints), baseline=new_baseline, - atomic=False ) def __mul__(self, Nelt): diff --git a/src/sage/typeset/character_art_factory.py b/src/sage/typeset/character_art_factory.py index beeb76c6d4b..8f3a263d68a 100644 --- a/src/sage/typeset/character_art_factory.py +++ b/src/sage/typeset/character_art_factory.py @@ -35,7 +35,8 @@ def __init__(self, INPUT: - - ``art_type`` -- type of the character art. + - ``art_type`` -- type of the character art (i.e. a subclass of + :class:`~sage.typeset.character_art.CharacterArt`) - ``string_type`` -- type of strings (the lines in the character art). @@ -213,7 +214,6 @@ def build_comma_sequence(self, iterable, func=None): not_first = False for elem in iterable: if not_first: - #print repr_elems.get_baseline() if repr_elems._baseline is not None: repr_elems += self.art_type( ["" ** ZZ(repr_elems._h - 1 - repr_elems._baseline) ] + @@ -302,7 +302,7 @@ def build_container(self, content, left_border, right_border): lines.append(left + pad + line.ljust(w) + pad + right) shift = len(left_border) + len(pad) basepoints = [bp + shift for bp in content.get_breakpoints()] + [w+shift] - return self.art_type(lines, basepoints, baseline=0, atomic=False) + return self.art_type(lines, basepoints, baseline=0) def build_list(self, list): r""" diff --git a/src/sage/typeset/unicode_art.py b/src/sage/typeset/unicode_art.py index 3e85815c482..276d3ded2fb 100644 --- a/src/sage/typeset/unicode_art.py +++ b/src/sage/typeset/unicode_art.py @@ -42,9 +42,6 @@ class UnicodeArt(CharacterArt): - ``baseline`` -- the reference line (from the bottom) - - ``atomic`` -- indicate if the ascii art representation is splittable - (must be coherent with breakpoints) - EXAMPLES:: sage: i = var('i') From 2b0207c21b4cade4fc1991f8b9bfbe75a5cd4d1c Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 24 May 2015 12:29:43 +0200 Subject: [PATCH 11/12] Trac #18357: get rid of build_comma_sequence - change the use of CharacterArtFactory.build_comma_sequence to use concatenate instead - fix the breakpoints in CharacterArtFactory.concatenate and CharacterArtFactory.build_container - move around CharacterArtFactory.build_set and CharacterArtFactory.build_dict --- src/sage/repl/display/formatter.py | 22 +-- src/sage/repl/ipython_extension.py | 14 +- src/sage/repl/rich_output/backend_base.py | 17 +- src/sage/typeset/character_art.py | 8 +- src/sage/typeset/character_art_factory.py | 187 +++++++++++----------- 5 files changed, 123 insertions(+), 125 deletions(-) diff --git a/src/sage/repl/display/formatter.py b/src/sage/repl/display/formatter.py index 39776cddab1..04bca36f77a 100644 --- a/src/sage/repl/display/formatter.py +++ b/src/sage/repl/display/formatter.py @@ -36,14 +36,14 @@ 10*x + 9*x + 8*x + 7*x + 6*x + 5*x + 4*x + 3*x + 2*x + x sage: shell.run_cell('StandardTableaux(4).list()') [ - [ 1 4 1 3 - [ 1 3 4 1 2 4 1 2 3 1 3 1 2 2 2 - [ 1 2 3 4, 2 , 3 , 4 , 2 4, 3 4, 3 , 4 + [ 1 4 + [ 1 3 4 1 2 4 1 2 3 1 3 1 2 2 + [ 1 2 3 4, 2 , 3 , 4 , 2 4, 3 4, 3 , - 1 ] - 1 2 2 ] - 3 3 ] - , 4 , 4 ] + 1 ] + 1 3 1 2 2 ] + 2 3 3 ] + 4 , 4 , 4 ] sage: shell.run_cell('%display default') sage: shell.quit() @@ -74,7 +74,7 @@ class SageDisplayFormatter(DisplayFormatter): def __init__(self, *args, **kwds): """ This is where the Sage rich objects are translated to IPython - + INPUT/OUTPUT: See the IPython documentation. @@ -126,7 +126,7 @@ def format(self, obj, include=None, exclude=None): sage: shell.quit() """ return self.dm.displayhook(obj) - + class SagePlainTextFormatter(PlainTextFormatter): @@ -138,7 +138,7 @@ def __init__(self, *args, **kwds): In particular, it correctly print lists of matrices or other objects (see :meth:`sage.structure.parent.Parent._repr_option`). - + .. warning:: This IPython formatter is NOT used. You could use it to @@ -151,7 +151,7 @@ def __init__(self, *args, **kwds): See the IPython documentation. EXAMPLES:: - + sage: from sage.repl.interpreter import get_test_shell sage: shell = get_test_shell() sage: shell.display_formatter.formatters['text/plain'] diff --git a/src/sage/repl/ipython_extension.py b/src/sage/repl/ipython_extension.py index aeb7896d13c..e15dbc723fb 100644 --- a/src/sage/repl/ipython_extension.py +++ b/src/sage/repl/ipython_extension.py @@ -230,14 +230,14 @@ def display(self, args): sage: shell.run_cell('%display ascii_art') sage: shell.run_cell('StandardTableaux(4).list()') [ - [ 1 4 1 3 - [ 1 3 4 1 2 4 1 2 3 1 3 1 2 2 2 - [ 1 2 3 4, 2 , 3 , 4 , 2 4, 3 4, 3 , 4 + [ 1 4 + [ 1 3 4 1 2 4 1 2 3 1 3 1 2 2 + [ 1 2 3 4, 2 , 3 , 4 , 2 4, 3 4, 3 , - 1 ] - 1 2 2 ] - 3 3 ] - , 4 , 4 ] + 1 ] + 1 3 1 2 2 ] + 2 3 3 ] + 4 , 4 , 4 ] sage: shell.run_cell('%display ascii_art 50') sage: shell.run_cell('StandardTableaux(4).list()') [ diff --git a/src/sage/repl/rich_output/backend_base.py b/src/sage/repl/rich_output/backend_base.py index 25b5350ba08..623772fd2f1 100644 --- a/src/sage/repl/rich_output/backend_base.py +++ b/src/sage/repl/rich_output/backend_base.py @@ -336,16 +336,13 @@ def ascii_art_formatter(self, obj, **kwds): sage: out OutputAsciiArt container sage: out.ascii_art - buffer containing 228 bytes + buffer containing 114 bytes sage: print(out.ascii_art.get()) - [ [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, - ] 22, 23, 24, 25, 26, 27, 28, 29 ] - sage: backend.ascii_art_formatter([1,2,3], concatenate=False).ascii_art.get() - '[ ]\n[ 1, 2, 3 ]' + '[ 1, 2, 3 ]' sage: backend.ascii_art_formatter([1,2,3], concatenate=True ).ascii_art.get() '1 2 3' """ @@ -387,16 +384,14 @@ def unicode_art_formatter(self, obj, **kwds): sage: out OutputUnicodeArt container sage: out.unicode_art - buffer containing 236 bytes + buffer containing 114 bytes sage: print(out.unicode_art.get()) - ⎡ - ⎣ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, - ⎤ - 22, 23, 24, 25, 26, 27, 28, 29 ⎦ + 22, 23, 24, 25, 26, 27, 28, 29 ] sage: backend.unicode_art_formatter([1,2,3], concatenate=False).unicode_art.get() - '\xe2\x8e\xa1 \xe2\x8e\xa4\n\xe2\x8e\xa3 1, 2, 3 \xe2\x8e\xa6' + '[ 1, 2, 3 ]' sage: backend.unicode_art_formatter([1,2,3], concatenate=True ).unicode_art.get() '1 2 3' """ diff --git a/src/sage/typeset/character_art.py b/src/sage/typeset/character_art.py index 1ab1effe537..b26e0d0a6a3 100644 --- a/src/sage/typeset/character_art.py +++ b/src/sage/typeset/character_art.py @@ -201,7 +201,7 @@ def get_breakpoints(self): sage: p5 = AsciiArt([" * ", " * * ", "*****"]) sage: aa = ascii_art([p3, p5]) sage: aa.get_breakpoints() - [2, 5, 6, 7, 12] + [6] """ return self._breakpoints @@ -261,14 +261,14 @@ def _split_repr_(self, size): sage: p3 = AsciiArt([" * ", "***"]) sage: p5 = AsciiArt([" * ", " * * ", "*****"]) sage: aa = ascii_art([p3, p5]) - sage: print aa._split_repr_(6) + sage: print aa._split_repr_(10) [ [ * - [ *** + [ ***, * ] * * ] - , ***** ] + ***** ] """ f_split = self._breakpoints[0]; i = 1 while i < len(self._breakpoints) and self._breakpoints[i] < size: diff --git a/src/sage/typeset/character_art_factory.py b/src/sage/typeset/character_art_factory.py index 8f3a263d68a..44140a9bbe7 100644 --- a/src/sage/typeset/character_art_factory.py +++ b/src/sage/typeset/character_art_factory.py @@ -18,8 +18,6 @@ #******************************************************************************* from sage.structure.sage_object import SageObject -from sage.rings.all import ZZ - class CharacterArtFactory(SageObject): @@ -39,18 +37,20 @@ def __init__(self, :class:`~sage.typeset.character_art.CharacterArt`) - ``string_type`` -- type of strings (the lines in the - character art). + character art, e.g. ``str`` or ``unicode``). - - ``magic_method_name`` -- name of the Sage magic method. + - ``magic_method_name`` -- name of the Sage magic method (e.g. + ``'_ascii_art_'`` or ``'_unicode_art_'``). - ``parenthesis`` -- left/right pair of two multi-line - symbols. The parenthesis, a.k.a. round brackets. + symbols. The parenthesis, a.k.a. round brackets (used for printing + tuples). - ``square_bracket`` -- left/right pair of two multi-line - symbols. The square_brackets. + symbols. The square_brackets (used for printing lists). - ``curly_brace`` -- left/right pair of two multi-line - symbols. The curly braces. + symbols. The curly braces (used for printing sets). EXAMPLES:: @@ -191,77 +191,6 @@ def build_from_string(self, obj): obj = unicode(obj).encode('utf-8') return self.art_type(obj.splitlines()) - def build_comma_sequence(self, iterable, func=None): - r""" - Build up a comma-separated sequence - - Auxiliary function for ``build_X`` where ``X`` is ``dict``, - ``set``, ``list``, or ``tuple``. - - EXAMPLES:: - - sage: from sage.typeset.ascii_art import _ascii_art_factory as factory - sage: out = factory.build_comma_sequence(list(DyckWords(3))); out - /\ - /\ /\ /\/\ / \ - /\/\/\, /\/ \, / \/\, / \, / \ - sage: type(out) - - """ - if func is None: - func = lambda elem, _: self.build(elem) - repr_elems = self.build_empty() - not_first = False - for elem in iterable: - if not_first: - if repr_elems._baseline is not None: - repr_elems += self.art_type( - ["" ** ZZ(repr_elems._h - 1 - repr_elems._baseline) ] + - [", "], - baseline=repr_elems._baseline) - else: - repr_elems += self.art_type( - (["" ** ZZ(repr_elems._h - 1) ] if repr_elems._h > 1 else [])+ - [", "], - baseline=repr_elems._baseline) - repr_elems._breakpoints.append(repr_elems._l - 1) - repr_elems += func(elem, iterable) - not_first = True - return repr_elems - - def build_set(self, set): - r""" - Return an character art output of a set. - - TESTS:: - - sage: ascii_art(set(DyckWords(3))) - { /\ } - { /\ /\/\ /\ / \ } - { / \/\, / \, /\/\/\, /\/ \, / \ } - """ - repr_elems = self.build_comma_sequence(set) - return self.build_container( - repr_elems, self.left_curly_brace, self.right_curly_brace) - - def build_dict(self, dict): - r""" - Return an character art output of a dictionnary. - - TESTS:: - - sage: ascii_art({i:dw for i,dw in enumerate(DyckWords(3))}) - { /\ } - { /\ /\ /\/\ / \ } - { 0:/\/\/\, 1:/\/ \, 2:/ \/\, 3:/ \, 4:/ \ } - """ - colon = self.art_type([self.string_type(':')], baseline=0) - def func(k, dict): - return self.build(k) + colon + self.build(dict[k]) - repr_elems = self.build_comma_sequence(dict, func) - return self.build_container( - repr_elems, self.left_curly_brace, self.right_curly_brace) - def build_container(self, content, left_border, right_border): r""" Return character art for a container @@ -287,11 +216,8 @@ def build_container(self, content, left_border, right_border): [ /\ /\ /\/\ / \ ] [ /\/\/\, /\/ \, / \/\, / \, / \ ] sage: l.get_breakpoints() - [2, 8, 9, 10, 16, 17, 18, 24, 25, 26, 32, 33, 34, 40] + [9, 17, 25, 33] """ - # new_mat = [] - # for line in repr_elems: - # new_mat.append(l_border + line + " "**ZZ(len(repr_elems) - len(line)) + r_border) w = content.width() h = content.height() left_border = left_border.character_art(h) @@ -301,10 +227,57 @@ def build_container(self, content, left_border, right_border): for left, line, right in zip(left_border, content, right_border): lines.append(left + pad + line.ljust(w) + pad + right) shift = len(left_border) + len(pad) - basepoints = [bp + shift for bp in content.get_breakpoints()] + [w+shift] + basepoints = [bp + shift for bp in content.get_breakpoints()] return self.art_type(lines, basepoints, baseline=0) - def build_list(self, list): + def build_set(self, s): + r""" + Return an character art output of a set. + + TESTS:: + + sage: ascii_art(set(DyckWords(3))) + { /\ } + { /\ /\/\ /\ / \ } + { / \/\, / \, /\/\/\, /\/ \, / \ } + """ + comma = self.art_type([self.string_type(', ')], baseline=0) + repr_elems = self.concatenate(s, comma) + return self.build_container( + repr_elems, self.left_curly_brace, self.right_curly_brace) + + def build_dict(self, d): + r""" + Return an character art output of a dictionnary. + + TESTS:: + + sage: d = ascii_art({i:dw for i,dw in enumerate(DyckWords(3))}) + sage: d + { /\ } + { /\ /\ /\/\ / \ } + { 0:/\/\/\, 1:/\/ \, 2:/ \/\, 3:/ \, 4:/ \ } + sage: d.get_breakpoints() + [11, 21, 31, 41] + """ + comma = self.art_type([self.string_type(', ')], + baseline=0, + breakpoints=[1]) + colon = self.art_type([self.string_type(':')], baseline=0) + def concat_no_breakpoint(k,v): + k = self.build(k) + v = self.build(v) + elt = k + colon + v + elt._breakpoints.remove(k._l) + elt._breakpoints.remove(k._l + 1) + return elt + repr_elems = self.concatenate( + (concat_no_breakpoint(k,v) for k,v in d.iteritems()), + comma) + return self.build_container(repr_elems, + self.left_curly_brace, self.right_curly_brace) + + def build_list(self, l): r""" Return an character art output of a list. @@ -315,13 +288,22 @@ def build_list(self, list): [ /\ /\ /\/\ / \ ] [ /\/\/\, /\/ \, / \/\, / \, / \ ] sage: l.get_breakpoints() - [2, 8, 9, 10, 16, 17, 18, 24, 25, 26, 32, 33, 34, 40] + [9, 17, 25, 33] + + The breakpoints of the object are used as breakpoints:: + + sage: l = ascii_art([DyckWords(2).list(), DyckWords(2).list()]) + sage: l.get_breakpoints() + [9, 17, 25] """ - repr_elems = self.build_comma_sequence(list) + comma = self.art_type([self.string_type(', ')], + baseline=0, + breakpoints=[1]) + repr_elems = self.concatenate(l, comma) return self.build_container( repr_elems, self.left_square_bracket, self.right_square_bracket) - def build_tuple(self, tuple): + def build_tuple(self, t): r""" Return an character art output of a tuple. @@ -332,14 +314,21 @@ def build_tuple(self, tuple): ( /\ /\ /\/\ / \ ) ( /\/\/\, /\/ \, / \/\, / \, / \ ) """ - repr_elems = self.build_comma_sequence(tuple) + comma = self.art_type([self.string_type(', ')], + baseline=0, + breakpoints=[1]) + repr_elems = self.concatenate(t, comma) return self.build_container( repr_elems, self.left_parenthesis, self.right_parenthesis) - def concatenate(self, iterable, separator, empty): + def concatenate(self, iterable, separator, empty=None): """ Concatenate multiple character art instances + The breakpoints are set as the breakpoints of the ``separator`` together + with the breakpoints of the objects in ``iterable``. If there is + ``None``, the end of the separator is used. + INPUT: - ``iterable`` -- iterable of character art. @@ -347,19 +336,33 @@ def concatenate(self, iterable, separator, empty): - ``separable`` -- character art. The separator in-between the iterable. - - ``empty`` -- the empty character art. + - ``empty`` -- an optional character art which is returned if + ``iterable`` is empty EXAMPLES:: - sage: i2 = identity_matrix(ZZ, 2) + sage: i2 = identity_matrix(2) sage: ascii_art(i2, i2, i2, sep=ascii_art(1/x)) 1 1 [1 0]-[1 0]-[1 0] [0 1]x[0 1]x[0 1] """ + if empty is None: + empty = self.build_empty() result = empty + breakpoints = [] + separator = self.build(separator) + bk = separator.get_breakpoints() + if not bk: + bk = [separator._l] for obj in iterable: if result is not empty: + l = result._l result += separator - result += obj + breakpoints.extend([l+x for x in bk]) + l = result._l + obj = self.build(obj) + result += self.build(obj) + breakpoints.extend([l+x for x in obj.get_breakpoints()]) + result._breakpoints = breakpoints return result From a6cf84fccb80789526cec661191c1643ec1d664d Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Sun, 31 May 2015 22:30:52 +0200 Subject: [PATCH 12/12] Trac #18357: fix doctests --- src/sage/combinat/binary_tree.py | 22 ++++++++++------------ src/sage/combinat/shuffle.py | 27 +++++++++++---------------- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/src/sage/combinat/binary_tree.py b/src/sage/combinat/binary_tree.py index b5385adf664..775144f47c8 100644 --- a/src/sage/combinat/binary_tree.py +++ b/src/sage/combinat/binary_tree.py @@ -1507,20 +1507,18 @@ def in_order_traversal_iter(self): [ / \ ] [ o o ] sage: ascii_art(list(b.in_order_traversal_iter())) - [ ] - [ , o, , _o_ o o o ] + [ , o, , _o_ , , o, , o , , o, ] [ / \ / \ ] [ o o o o ] [ / \ ] - [ o o, , , , , , , ] + [ o o ] sage: ascii_art(filter(lambda node: node.label() is not None, ....: b.canonical_labelling().in_order_traversal_iter())) - [ ] - [ 1, _2_ 3 4 5 ] + [ 1, _2_ , 3, 4 , 5 ] [ / \ / \ ] [ 1 4 3 5 ] [ / \ ] - [ 3 5, , , ] + [ 3 5 ] sage: list(BinaryTree(None).in_order_traversal_iter()) [.] @@ -2413,11 +2411,11 @@ def over(self, bt): sage: b1 = BinaryTree([[],[[],[]]]) sage: b2 = BinaryTree([[None, []],[]]) sage: ascii_art((b1, b2, b1/b2)) - ( _o_ _o_ _o_ ) + ( _o_ , _o_ , _o_ ) ( / \ / \ / \ ) ( o o o o o o_ ) ( / \ \ / \ ) - ( o o, o , o o ) + ( o o o o o ) ( \ ) ( _o_ ) ( / \ ) @@ -2509,9 +2507,9 @@ def under(self, bt): sage: b1 = BinaryTree([[],[]]) sage: b2 = BinaryTree([None,[]]) sage: ascii_art((b1, b2, b1 \ b2)) - ( o o _o_ ) + ( o , o , _o_ ) ( / \ \ / \ ) - ( o o, o, o o ) + ( o o o o o ) ( / \ ) ( o o ) @@ -2771,11 +2769,11 @@ def is_full(self): sage: BinaryTree([[[[],[]],[[],[]]], []]).is_full() True sage: ascii_art(filter(lambda bt: bt.is_full(), BinaryTrees(5))) - [ _o_ _o_ ] + [ _o_ , _o_ ] [ / \ / \ ] [ o o o o ] [ / \ / \ ] - [ o o, o o ] + [ o o o o ] """ if self.is_empty(): return True diff --git a/src/sage/combinat/shuffle.py b/src/sage/combinat/shuffle.py index fd29264bd00..47f0896cc11 100644 --- a/src/sage/combinat/shuffle.py +++ b/src/sage/combinat/shuffle.py @@ -154,9 +154,8 @@ def _ascii_art_(self): sage: ascii_art(SetShuffleProduct([[BinaryTree()], [BinaryTree([]), BinaryTree([[],[]])]], ....: [[1,4]])) Set shuffle product of: - [ [ ] ] [ [ o, o ] ] - [ [ / \ ] ] [ [ ] ] + [ [ / \ ] ] [ [ ], [ o o ] ] and [ [ 1, 4 ] ] """ @@ -292,14 +291,13 @@ def _ascii_art_(self): sage: from sage.combinat.shuffle import ShuffleProduct sage: ascii_art(ShuffleProduct([1,2,3],[4,5])) Shuffle product of: - [ ] [ ] [ 1, 2, 3 ] and [ 4, 5 ] sage: B = BinaryTree sage: ascii_art(ShuffleProduct([B([]), B([[],[]])], ....: [B([[[],[]],[[],None]])])) Shuffle product of: [ __o__ ] - [ ] [ / \ ] + [ / \ ] [ o, o ] [ o o ] [ / \ ] [ / \ / ] [ o o ] and [ o o o ] @@ -324,20 +322,17 @@ def __iter__(self): sage: B = BinaryTree sage: ascii_art(list(ShuffleProduct([B([]), B([[],[]])], ....: [B([[[],[]],[[],None]])]))) - [ [ ] [ - [ [ o, o __o__ ] [ __o__ o o ] [ o, __o__ o - [ [ / \ / \ ] [ / \ / \ ] [ / \ / \ - [ [ o o, o o ] [ o o o o ] [ o o o o + [ [ o, o , __o__ ] [ __o__ , o, o ] [ o, __o__ , + [ [ / \ / \ ] [ / \ / \ ] [ / \ + [ [ o o o o ] [ o o o o ] [ o o [ [ / \ / ] [ / \ / ] [ / \ / - [ [ o o o ], [ o o o , , ], [ o o o , + [ [ o o o ], [ o o o ], [ o o o - ] ] - ] ] - ] ] - ] ] - ] ] - ] ] - + o ] ] + / \ ] ] + o o ] ] + ] ] + ] ] """ ############ Gray code #############