diff options
Diffstat (limited to 'macros/latex/contrib/atableau/atableau.sty')
-rw-r--r-- | macros/latex/contrib/atableau/atableau.sty | 3335 |
1 files changed, 3335 insertions, 0 deletions
diff --git a/macros/latex/contrib/atableau/atableau.sty b/macros/latex/contrib/atableau/atableau.sty new file mode 100644 index 0000000000..75904e3c64 --- /dev/null +++ b/macros/latex/contrib/atableau/atableau.sty @@ -0,0 +1,3335 @@ +% --------------------------------------------------------------------------- +% +% atableau - Andrew Mathas (C) 2022-2025 +% +% A latex package for symmetric group combinatorics, including: +% - abacuses +% - multitableau +% - ribbon tableau +% - shifted tableau +% - skew tableau +% - tableaux +% - tabloids +% - Young diagrams +% +% E-mail: andrew.mathas@gmail.com +% Released under the LaTeX Project Public License v1.3c or later +% See http://www.latex-project.org/lppl.txt + +% --------------------------------------------------------------------------- +% load TikZ early to avoid \ExplSyntaxOn...\ExplSyntaxOff wrappers +\RequirePackage{tikz} +\usetikzlibrary{shapes.geometric,matrix} + + +% Correct for negative signs in contents being too long to fit in a tableau +\RequirePackage{amsfonts} %% <- also included by amssymb +\DeclareMathSymbol{\shortminus}{\mathbin}{AMSa}{"39} + +% --------------------------------------------------------------------------- +% package date and version +\def\atableau@version{2.0.2} +\def\atableau@release{2025-01-22} + +\providecommand\DeclareRelease[3]{} +\providecommand\DeclareCurrentRelease[2]{} +\DeclareRelease{\atableau@version}{\atableau@release}{atableau.sty} +\DeclareCurrentRelease{}{\atableau@release} + +\ProvidesExplPackage{atableau} {\atableau@release} {\atableau@version} + {A latex package for symmetric group combinatorics } + +% --------------------------------------------------------------------------- +% package errors + +\msg_new:nnnn { aTableau } { empty-tableau-row } + { Row~#1~of~tableau~is~empty } + { Empty~tableau~rows~are~not~supported} + +\msg_new:nnnn { aTableau } { missing-runner-labels } +{ Your~abacus~has~a~different~number~of~runners~and~runner~labels } +{ The~number~of~labels~given~to~the~'runner~labels'~must~match~the~number~of~abacus~runners } + +\msg_new:nnnn { aTableau } { missing-style } +{ The~'#1'~styles~key~is~missing~a~value } +{ The~styles~key~accepts~a~comma~separated~list~of~key-value~pairs~for~defining~TikZ~styles } + +\msg_new:nnnn { aTableau } { invalid-dots} + { Invalid~specifications~for~dotted~rows~or~dotted~cols } + { You~can~only~use~these~options~for~interior~rows~and~columns~in~the~diagrams} + +\msg_new:nnnn { aTableau } { invalid-ribbon-head } + { Invalid~ribbon~head:~the~row~and~column~indices~of~the~ribbon~head~must~be~given:~'#1'} + { Ribbon~specifications~must~be~for~the~form:~(optional~ribbon~style)[style]<row><col>+sequences~of~r's~and~c's~with~style} + +\msg_new:nnnn { aTableau } { invalid-ribbon-specification } + { Invalid~ribbon~specification~'#1':~expecting~r~or~c. } + { Ribbon~specifications~must~be~for~the~form:~[optional~style]<row><col>~sequences~of~r's and~c's~with~style} + +\msg_new:nnnn { aTableau } { unrecognised-abacus-label } + { Unrecognised~abacus~label~'#1'. } + { The~possible~abacus~labels~are~betas,~residues,~rows~and~shape} + +\msg_new:nnnn { aTableau } { unrecognised-entries } + { Unrecognised~entries~value:~'#1' } + { The~possible~diagram~values~for~entries~are:~contents,~first,~hooks,~last,~and~residues} + +\msg_new:nnnn { aTableau } { unrecognised-style } + { Unrecognised~style:~'#1' } + { The~available~tableau~styles~are~australian,~english,~french~and~ukrainian~and~the~available~abacus~styles~are~north,~south,~east~and~west~} + +\msg_new:nnnn { aTableau } { unknown-abacus-ends } + { Unrecognised~abacus~ends~setting:~'#1' } + { The~supported~ends~for~the~top/bottom~of~the~abacus~are:~-,~_~,.~,|,~and~>} + +\msg_new:nnnn { aTableau } { unknown-cartan } + { Unrecognised~Cartan~type~'#1' } + { The~supported~Cartan~types~are~(affine)~types~A,~AA,~C~and~DD} + +\msg_new:nnnn { aTableau } { unknown-baseline } + { Unrecognised~baseline~option:~'#1' } + { The~supported~halign~options~are~bottom,~centre,~left,~and~top } + +\msg_new:nnnn { aTableau } { unknown-halign } + { Unrecognised~halign~option:~'#1' } + { The~supported~halign~options~are~centre,~left,~and~right } + +\msg_new:nnnn { aTableau } { unknown-top } + { Unrecognised~format~for~abacus~top;~'#1' } + { The~supported~formats~are:~blank,~dots,~and~frame} + +\msg_new:nnnn { aTableau } { unknown-valign } + { Unrecognised~valign~option:~#1 } + { The~supported~valign~options~are~bottom,~centre~and~top } + +% --------------------------------------------------------------------------- +% package variables + +\bool_new:N \l__atableau_beta_numbers_bool% true if specifying beta numbers +\bool_new:N \l__atableau_frame_bool % true if drawing framed abacus runners +\bool_new:N \l__atableau_conjugate_bool % true if drawing conjugate tableau/diagram +\bool_new:N \l__atableau_border_bool % true if drawing tableau border +\bool_new:N \l__atableau_boxes_bool % true if drawing inner tableau walls +\bool_new:N \l__atableau_shifted_bool % true if a shifted tableau +\bool_new:N \l__atableau_skew_border_bool % true if drawing skew border +\bool_new:N \l__atableau_skew_boxes_bool % true if drawing skew boxes +\bool_new:N \l__atableau_tabloid_bool % true if a tabloid +\bool_new:N \l__atableau_separators_bool % true if drawing separators for multishapes + +\fp_new:N \l__atableau_ab_col_dx_fp % change in x-coordinate between columns in an abacus +\fp_new:N \l__atableau_ab_col_dy_fp % change in y-coordinate between columns in an abacus +\fp_new:N \l__atableau_ab_row_dx_fp % change in x-coordinate between rows in an abacus +\fp_new:N \l__atableau_ab_row_dy_fp % change in y-coordinate between rows in an abacus +\fp_new:N \l__atableau_abacus_ht_fp % height of the tableaux nodes/separation between abacus beads +\fp_new:N \l__atableau_abacus_wd_fp % width of the tableaux nodes/separation between abacus runners +\fp_new:N \l__atableau_bead_size_fp % bead radius +\fp_new:N \l__atableau_box_ht_fp % height of a tableau box +\fp_new:N \l__atableau_box_wd_fp % width of a tableau box +\fp_new:N \l__atableau_rows_fp % number of rows in abacus/tableau +\fp_new:N \l__atableau_separation_fp % distance between multitableau +\fp_new:N \l__atableau_tab_col_dx_fp % change in x-coordinate between columns in a tableau +\fp_new:N \l__atableau_tab_col_dy_fp % change in y-coordinate between columns in a tableau +\fp_new:N \l__atableau_tab_row_dx_fp % change in x-coordinate between rows in a tableau +\fp_new:N \l__atableau_tab_row_dy_fp % change in y-coordinate between rows in a tableau +\fp_new:N \l__atableau_tick_length_fp % half the length of the abacus ticks +\fp_new:N \l__atableau_x_fp % x-coordinate of the origin of the diagram +\fp_new:N \l__atableau_xa_fp % scratch x-coordinate of a node/bead +\fp_new:N \l__atableau_xb_fp % scratch x-coordinate of a node/bead +\fp_new:N \l__atableau_xl_fp % x-coordinate of a tableau node/bead +\fp_new:N \l__atableau_xmax_fp % maximum x-coordinate for multidiagrams and multitableaux +\fp_new:N \l__atableau_xoffsets_seq % x-offsets for the (1,1)-nodes in a multi-tableau +\fp_new:N \l__atableau_xscale_fp % scaling in the x-direction +\fp_new:N \l__atableau_xsep_fp % x-coordinate difference to next separator for multishapes +\fp_new:N \l__atableau_y_fp % y-coordinate of the origin of the diagram +\fp_new:N \l__atableau_ya_fp % scratch y-coordinate of a node/bead +\fp_new:N \l__atableau_yb_fp % scratch y-coordinate of a node/bead +\fp_new:N \l__atableau_yl_fp % y-coordinate of a tableau node/bead +\fp_new:N \l__atableau_ymax_fp % maximum y-coordinate for multidiagrams and multitableaux +\fp_new:N \l__atableau_ymin_fp % minimum y-coordinate for multidiagrams and multitableaux +\fp_new:N \l__atableau_yoffsets_seq % t-offsets for the (1,1)-nodes in a multi-tableau +\fp_new:N \l__atableau_yscale_fp % scaling in the x-direction + +\int_new:N \l__atableau_beads_int % number of beads in abacus +\int_new:N \l__atableau_charge_int % charge for current component +\int_new:N \l__atableau_col_int % current column index +\int_new:N \l__atableau_c_int % scratch column counter +\int_new:N \l__atableau_cols_int % number of columns in a multitableau/abacus +\int_new:N \l__atableau_component_int % component in multidiagrams and tableaux +\int_new:N \l__atableau_e_int % quantum characteristic +\int_new:N \l__atableau_row_int % current row index +\int_new:N \l__atableau_r_int % scratch row counter +\int_new:N \l__atableau_rows_int % number of rows in abacus + +\seq_new:N \l__atableau_charge_seq % sequences of charges = residue/content offset +\seq_new:N \l__atableau_component_seq % multipartition +\seq_new:N \l__atableau_conjugate_seq % conjugate partition +\seq_new:N \l__atableau_dotted_cols_seq % columns with dots +\seq_new:N \l__atableau_dotted_rows_seq % rows with dots +\seq_new:N \l__atableau_multidotted_cols_seq % sequence of dotted columns for multi shapes +\seq_new:N \l__atableau_multidotted_rows_seq % sequence of dotted rows for multi shapes +\seq_new:N \l__atableau_multilabel_seq % a sequence of labels for multi shapes +\seq_new:N \l__atableau_multipaths_seq % a sequence of ribbon paths for multi shapes +\seq_new:N \l__atableau_multiribbons_seq % a sequence of ribbon for multi shapes +\seq_new:N \l__atableau_multiskew_seq % a sequence of skew partitions for multi shapes +\seq_new:N \l__atableau_multisnobs_seq % a sequence of snobs for multi shapes +\seq_new:N \l__atableau_paths_seq % ribbon paths to add to tableau/diagram +\seq_new:N \l__atableau_rcs_seq % ribbon row, column indices +\seq_new:N \l__atableau_ribbons_seq % ribbons to add to tableau/diagram +\seq_new:N \l__atableau_runner_labels_seq % labels for the abacus runners +\seq_new:N \l__atableau_shape_seq % a partition +\seq_new:N \l__atableau_skew_seq % the inner partition for a skew shape +\seq_new:N \l__atableau_snobs_seq % snob ribbons to add to tableau/diagram +\seq_new:N \l__atableau_styles_seq % ribbon/bead styles +\seq_new:N \l__atableau_texts_seq % ribbon/bead texts +\seq_new:N \l__atableau_xsep_seq % x-coordinates separators for multishapes + +\tl_new:N \l__atableau_abacus_bottom_tl % specifies the bottom of the abacus +\tl_new:N \l__atableau_abacus_top_tl % specifies the top of the abacus +\tl_new:N \l__atableau_bead_font_tl % font for abacus beads +\tl_new:N \l__atableau_bead_shape_tl % shape of abacus beads +\tl_new:N \l__atableau_bead_text_tl % text colour of abacus beads +\tl_new:N \l__atableau_bead_tl % abacus head colour +\tl_new:N \l__atableau_border_tl % diagram border +\tl_new:N \l__atableau_box_fill_tl % fill colour for tableau boxes +\tl_new:N \l__atableau_box_font_tl % font for tableau boxes +\tl_new:N \l__atableau_box_shape_tl % shape of tableau boxes +\tl_new:N \l__atableau_box_text_tl % text colour for tableau boxes +\tl_new:N \l__atableau_capture_exp_tl % contains captured exponent +\tl_new:N \l__atableau_capture_part_tl % contains captured part +\tl_new:N \l__atableau_capture_style_tl % contains captured style +\tl_new:N \l__atableau_capture_txt_tl % contains captured text +\tl_new:N \l__atableau_cartan_tl % the Cartan type +\tl_new:N \l__atableau_empty_tl % symbol for empty tableau/diagram in a multi tableau/diagram +\tl_new:N \l__atableau_entries_tl % custom entries in tableau +\tl_new:N \l__atableau_inner_tl % colour of tableau inner walls +\tl_new:N \l__atableau_label_tl % a label to print on an tableau/diagram +\tl_new:N \l__atableau_left_delimiter_tl % the left delimiter for multitableau and multidiagrams +\tl_new:N \l__atableau_multiprefix_tl % prefix used when constructing multinode names +\tl_new:N \l__atableau_name_tl % name of a tableau node +\tl_new:N \l__atableau_outer_tl % colour of tableau outer walls +\tl_new:N \l__atableau_path_box_tl % node for ribbon paths +\tl_new:N \l__atableau_prefix_tl % prefix for node names +\tl_new:N \l__atableau_ribbon_box_tl % box entry for ribbons +\tl_new:N \l__atableau_ribbon_style_tl % optional style used for current path< ribbon or snob +\tl_new:N \l__atableau_ribbon_path_tl % a ribbon in the ribbon tableau +\tl_new:N \l__atableau_ribbon_type_tl % the type of ribbon, which is either path, ribbon or snob +\tl_new:N \l__atableau_right_delimiter_tl % the right delimiter for multitableau and multidiagrams +\tl_new:N \l__atableau_runner_tl % abacus runner colour +\tl_new:N \l__atableau_snob_box_tl % box entry for snobs +\tl_new:N \l__atableau_separator_fg_tl % foreground colour of the separator +\tl_new:N \l__atableau_separator_tl % the separator between multitableau: | or , or ... +\tl_new:N \l__atableau_shading_tl % the type of shading to use for the abacus beads +\tl_new:N \l__atableau_show_tl % automatic tableau/bead labelling +\tl_new:N \l__atableau_skew_border_tl % skew border colour +\tl_new:N \l__atableau_starstyle_tl % current star style in use +\tl_new:N \l__atableau_styled_nodes_tl % token lists of nodes with non-default style +\tl_new:N \l__atableau_tick_tl % abacus tick colour +\tl_new:N \l__atableau_tikz_after_tl % TikZ commands for after the diagram +\tl_new:N \l__atableau_tikz_before_tl % TikZ commands for before the diagram +\tl_new:N \l__atableau_tikzpicture_tl % TikZ environment settings +\tl_new:N \l__atableau_unstyled_nodes_tl % token lists of nodes with efault style + +% --------------------------------------------------------------------------- +% Variable defaults -- most defaults are given in the settings: \keys_set:nn {aTableau} {...} +\tl_set:Nn \l__atableau_ribbon_type_tl {ribbon} % by default, ribbons are ribbons + +% --------------------------------------------------------------------------- +% default colours +\definecolor{aTableauMain} {HTML} {00008B} +\definecolor{aTableauInner} {HTML} {0073e6} +\definecolor{aTableauSkew} {HTML} {818589} +\definecolor{aTableauSkewFill} {HTML} {F8F8F8} +\definecolor{aTableauStarStyle} {HTML} {E6F7FF} + +% --------------------------------------------------------------------------- +% TikZ styling of aTableau components + +\tikzset{ + % ----------------------------------------------------------------------- + % Allow ball shading to be disabled via shade=none. + % The essential idea comes from https://tex.stackexchange.com/a/85750/234252 + no~shade/.code={ \tikz@addmode{\tikz@mode@shadefalse } }, + % ----------------------------------------------------------------------- + % all tikz settings are in the aTableau family + aTableau/.is~family, + aTableau/.cd, + % ----------------------------------------------------------------------- + % Styles for tableaux and diagrams + % ----------------------------------------------------------------------- + % inner tableau wall + innerWall/.style = { + line~cap = rect, + thin, + }, + % outer tableau wall + borderStyle/.style = { + % shifting allows us to use \__atableau_set_box_coordinates:nnn when drawing the border + shift = {(\fp_eval:n{-(\l__atableau_tab_row_dx_fp+\l__atableau_tab_col_dx_fp)*\l__atableau_box_wd_fp/2}, + \fp_eval:n{-(\l__atableau_tab_row_dy_fp+\l__atableau_tab_col_dy_fp)*\l__atableau_box_ht_fp/2})}, + line~cap = rect, + very~thick, + draw = \l__atableau_outer_tl, + }, + % skew walls + skewBorder/.style = { + % shifting allows us to use \__atableau_set_box_coordinates:nnn when drawing the border + shift = {(\fp_eval:n{-(\l__atableau_tab_row_dx_fp+\l__atableau_tab_col_dx_fp)*\l__atableau_box_wd_fp/2}, + \fp_eval:n{-(\l__atableau_tab_row_dy_fp+\l__atableau_tab_col_dy_fp)*\l__atableau_box_ht_fp/2})}, + draw = \l__atableau_skew_border_tl, + thick + }, + % styles for tableau boxes in Young diagrams + % ----------------------------------------------------------------------- + node/.style = { + anchor = center, + inner~sep = 0pt, + minimum~height = \fp_use:N \l__atableau_box_ht_fp cm, + minimum~width = \fp_use:N \l__atableau_box_wd_fp cm, + shape = \l__atableau_box_shape_tl, + font = \l__atableau_box_font_tl, + text = \l__atableau_box_text_tl, + }, + boxStyle/.style = { + aTableau/node, + aTableau/innerWall, + draw = \l__atableau_inner_tl, + fill = \l__atableau_box_fill_tl, + }, + % default skew box style + skewBox/.style = { + aTableau/node, + aTableau/innerWall, + draw = \l__atableau_skew_border_tl, + fill = aTableauSkewFill, + }, + % box styles for paths, ribbons and snobs + % ----------------------------------------------------------------------- + pathBox/.style = { + aTableau/node, + draw = none, % border disabled by default + }, + ribbonBox/.style = { + aTableau/node, + }, + snobBox/.style = { + aTableau/node, + }, + % styles for paths, ribbons and snobs + % ----------------------------------------------------------------------- + % default path style + pathStyle/.style = { + draw = \l__atableau_inner_tl, + }, + ribbonStyle/.style = { + aTableau/innerWall, + draw = \l__atableau_inner_tl, + }, + % default ribbon style + snobStyle/.style = { + aTableau/innerWall, + draw = \l__atableau_inner_tl, + }, + % label styles + % ----------------------------------------------------------------------- + labelStyle/.style = { + shift = {(\fp_eval:n{-0.2*(\l__atableau_tab_row_dx_fp+\l__atableau_tab_col_dx_fp)*\l__atableau_box_wd_fp}, + \fp_eval:n{-0.2*(\l__atableau_tab_row_dy_fp+\l__atableau_tab_col_dy_fp)*\l__atableau_box_ht_fp})}, + font=\scriptsize, + text = \l__atableau_inner_tl, + }, + % tableau star style + % ----------------------------------------------------------------------- + tableauStarStyle/.style = { + fill = aTableauStarStyle, + text = \l__atableau_box_text_tl, + }, + % cleared boxes for dotted rows and columns + % ----------------------------------------------------------------------- + clearBoxes/.style = { + draw = white, + fill = white, + }, + % dots used for dotted rows and columns + % ----------------------------------------------------------------------- + dottedLine/.style = { + densely~dotted, + thick, + draw = \l__atableau_outer_tl, + }, + % separators and delimiters + % ----------------------------------------------------------------------- + separatorSymbol/.style = { + text = \l__atableau_separator_fg_tl, + }, + % default separator line style + separatorLine/.style = { + thick, + draw = \l__atableau_separator_fg_tl, + }, + % Delimiters around multitableaux and multidiagrams. To change the colour + % of the delimiters we need to use \path[aTableau/delimiterPath] (x,y) node... + delimiterStyle/.style = { + align = center, + inner~sep = 0pt, + minimum~height = \fp_use:N\l__atableau_ymax_fp cm, + }, + % hack to set the colour + delimiterPath/.style = { + every~delimiter/.style = { \l__atableau_separator_fg_tl, }, + }, + leftDelimiter/.style = { + aTableau/delimiterStyle, + left~delimiter = \l__atableau_left_delimiter_tl, + xshift = 2pt, + }, + rightDelimiter/.style = { + aTableau/delimiterStyle, + right~delimiter = \l__atableau_right_delimiter_tl, + xshift = -2pt, + }, + % ----------------------------------------------------------------------- + % abacus beads and runners + % ----------------------------------------------------------------------- + % abacus beads + beadStyle/.style = { + ball~color = \l__atableau_bead_tl, + font = \l__atableau_bead_font_tl, + minimum~height = \fp_to_decimal:N \l__atableau_bead_size_fp cm, + minimum~width = \fp_to_decimal:N \l__atableau_bead_size_fp cm, + shading = \l__atableau_shading_tl, + shape = \l__atableau_bead_shape_tl, + text = \l__atableau_bead_text_tl, + anchor = center, + inner~sep = 0pt, + }, + % abacus runners + runnerStyle/.style = { + line~cap = rect, + very~thick, + draw = \l__atableau_runner_tl, + }, + runnerLabelStyle/.style = { + aTableau/node, + text = aTableauInner, + font = \scriptsize + }, + % style for the top and bottom of the abacus + abacusEnds/.style = { + aTableau/runnerStyle, + }, + % abacus star style + abacusStarStyle/.style = { + text = aTableauMain, + ball~color = aTableauStarStyle, + }, + % abacus ticks + tickStyle/.style = { + draw = \l__atableau_tick_tl, + semithick, + }, + % the named coordinate for an abacus tick + namedTick/.style = { + minimum~height = \fp_to_decimal:N \l__atableau_bead_size_fp cm, + minimum~width = \fp_to_decimal:N \l__atableau_bead_size_fp cm, + shape = \l__atableau_bead_shape_tl, + draw=none, + } +} + +% --------------------------------------------------------------------------- + +% usage: \__atableau_set_style:nn {abacus|tableau} {style} +% sets the basic styles for the tableaux and abacus commands +\cs_new:Npn \__atableau_set_style:nn #1 #2 +{ + \str_case:enF { #1/#2 } + { + {tableau/english} + { + \fp_set:Nn \l__atableau_tab_col_dx_fp {1} + \fp_set:Nn \l__atableau_tab_col_dy_fp {0} + \fp_set:Nn \l__atableau_tab_row_dx_fp {0} + \fp_set:Nn \l__atableau_tab_row_dy_fp {-1} + \fp_set:Nn \l__atableau_box_ht_fp {\l__atableau_yscale_fp*0.5} + \fp_set:Nn \l__atableau_box_wd_fp {\l__atableau_xscale_fp*0.5} + \tl_set:Nn \l__atableau_box_shape_tl {rectangle} + } + {tableau/french} + { + \fp_set:Nn \l__atableau_tab_col_dx_fp {1} + \fp_set:Nn \l__atableau_tab_col_dy_fp {0} + \fp_set:Nn \l__atableau_tab_row_dx_fp {0} + \fp_set:Nn \l__atableau_tab_row_dy_fp {1} + \fp_set:Nn \l__atableau_box_ht_fp {\l__atableau_yscale_fp*0.5} + \fp_set:Nn \l__atableau_box_wd_fp {\l__atableau_xscale_fp*0.5} + \tl_set:Nn \l__atableau_box_shape_tl {rectangle} + } + {tableau/australian} + { + \fp_set:Nn \l__atableau_tab_col_dx_fp {0.5} + \fp_set:Nn \l__atableau_tab_col_dy_fp {-0.5} + \fp_set:Nn \l__atableau_tab_row_dx_fp {-0.5} + \fp_set:Nn \l__atableau_tab_row_dy_fp {-0.5} + \fp_set:Nn \l__atableau_box_ht_fp {\l__atableau_yscale_fp*0.7012} % 1/sqrt(2) + \fp_set:Nn \l__atableau_box_wd_fp {\l__atableau_xscale_fp*0.7012} + \tl_set:Nn \l__atableau_box_shape_tl {diamond} + } + {tableau/ukrainian} + { + \fp_set:Nn \l__atableau_tab_col_dx_fp {0.5} + \fp_set:Nn \l__atableau_tab_col_dy_fp {0.5} + \fp_set:Nn \l__atableau_tab_row_dx_fp {-0.5} + \fp_set:Nn \l__atableau_tab_row_dy_fp {0.5} + \fp_set:Nn \l__atableau_box_ht_fp {\l__atableau_yscale_fp*0.7012} + \fp_set:Nn \l__atableau_box_wd_fp {\l__atableau_xscale_fp*0.7012} + \tl_set:Nn \l__atableau_box_shape_tl {diamond} + } + {tableau/russian} + { + \__atableau_set_style:nn {tableau} {ukrainian} + } + {abacus/south} + { + \fp_set:Nn \l__atableau_ab_col_dx_fp {1} + \fp_set:Nn \l__atableau_ab_col_dy_fp {0} + \fp_set:Nn \l__atableau_ab_row_dx_fp {0} + \fp_set:Nn \l__atableau_ab_row_dy_fp {-1} + \fp_set:Nn \l__atableau_abacus_ht_fp {\l__atableau_yscale_fp*0.5} + \fp_set:Nn \l__atableau_abacus_wd_fp {\l__atableau_xscale_fp*0.5} + \tl_set:Nn \l__atableau_bead_shape_tl {circle} + } + {abacus/north} + { + \fp_set:Nn \l__atableau_ab_col_dx_fp {1} + \fp_set:Nn \l__atableau_ab_col_dy_fp {0} + \fp_set:Nn \l__atableau_ab_row_dx_fp {0} + \fp_set:Nn \l__atableau_ab_row_dy_fp {1} + \tl_set:Nn \l__atableau_bead_shape_tl {circle} + \fp_set:Nn \l__atableau_abacus_wd_fp {\l__atableau_xscale_fp*0.5} + \fp_set:Nn \l__atableau_abacus_ht_fp {\l__atableau_yscale_fp*0.5} + } + {abacus/east} + { + \fp_set:Nn \l__atableau_ab_col_dx_fp {0} + \fp_set:Nn \l__atableau_ab_col_dy_fp {-1} + \fp_set:Nn \l__atableau_ab_row_dx_fp {1} + \fp_set:Nn \l__atableau_ab_row_dy_fp {0} + \tl_set:Nn \l__atableau_bead_shape_tl {circle} + \fp_set:Nn \l__atableau_abacus_wd_fp {\l__atableau_xscale_fp*0.5} + \fp_set:Nn \l__atableau_abacus_ht_fp {\l__atableau_yscale_fp*0.5} + } + {abacus/west} + { + \fp_set:Nn \l__atableau_ab_col_dx_fp {0} + \fp_set:Nn \l__atableau_ab_col_dy_fp {1} + \fp_set:Nn \l__atableau_ab_row_dx_fp {-1} + \fp_set:Nn \l__atableau_ab_row_dy_fp {0} + \tl_set:Nn \l__atableau_bead_shape_tl {circle} + \fp_set:Nn \l__atableau_abacus_wd_fp {\l__atableau_xscale_fp*0.5} + \fp_set:Nn \l__atableau_abacus_ht_fp {\l__atableau_yscale_fp*0.5} + } + } + { + \msg_error:nnx {aTableau} {unrecognised-style} {#1/#2} + } +} + +% usage: \__atableau_set_xscale:n {x-scale} : rescale the x-dimension +\cs_new_protected:Npn \__atableau_set_xscale:n #1 +{ + \fp_set:Nn \l__atableau_xscale_fp {#1} % makes scale persistent + \fp_set:Nn \l__atableau_abacus_wd_fp {#1*\l__atableau_abacus_wd_fp} + \fp_set:Nn \l__atableau_box_wd_fp {#1*\l__atableau_box_wd_fp} + \fp_set:Nn \l__atableau_separation_fp {#1*\l__atableau_separation_fp } +} +% usage: \__atableau_set_yscale:n {y-scale} : rescale the y-dimension +\cs_new_protected:Npn \__atableau_set_yscale:n #1 +{ + \fp_set:Nn \l__atableau_yscale_fp {#1} % makes scale persistent + \fp_set:Nn \l__atableau_abacus_ht_fp {#1*\l__atableau_abacus_ht_fp} + \fp_set:Nn \l__atableau_box_ht_fp {#1*\l__atableau_box_ht_fp} +} + + +% usage: \__atableau_set_delimiters:nn {left delimiter} {right delimiter} +\cs_new_protected:Npn \__atableau_set_delimiters:nn #1 #2 +{ + \tl_set:Nn \l__atableau_left_delimiter_tl #1 + \tl_set:Nn \l__atableau_right_delimiter_tl #2 +} + +% usage: \__atableau_set_multiseq_key:nn {name} {value} +% Set up a sequence, for keys used with multishapes +\cs_new_protected:Npn \__atableau_set_multiseq_key:nn #1 #2 +{ + \tl_if_in:nnTF {#2} {|} + { + % unpack the ribbons into \l__atableau_multiribbons_seq + \seq_set_split:cnn {l__atableau_multi#1_seq} {|} {#2} + } + { \seq_set_from_clist:cn {l__atableau_#1_seq} {#2} } +} + +% usage: \__atableau_set_abacus_ends:nn {top} {top} +% Set the abacus ends abacus_top and abacus_bottom and give an error +% message if the ends are not one of =,-,.,|,> +\cs_new_protected:Npn \__atableau_set_abacus_ends:nn #1 #2 +{ + \tl_set:Nn \l__atableau_abacus_top_tl {#1} + \str_if_in:nnF {-.>|\c_underscore_str*} {#1} + { \msg_error:nne {aTableau} {unknown-abacus-ends} {#1}} + + \tl_set:Nn \l__atableau_abacus_bottom_tl {#2} + \str_if_in:nnF {-.>|\c_underscore_str*} {#2} + { \msg_error:nne {aTableau} {unknown-abacus-ends} {#2}} +} + +% --------------------------------------------------------------------------- +% command variants + +\cs_generate_variant:Nn \fp_add:Nn {NV} +\cs_generate_variant:Nn \int_set:Nn {No} +\cs_generate_variant:Nn \seq_put_right:Nn {Nx} +\cs_generate_variant:Nn \seq_set_item:Nnn {NVx, Nnx, Nox} +\cs_generate_variant:Nn \seq_set_from_clist:Nn {co} +\cs_generate_variant:Nn \seq_set_split:Nnn {cnn} + +\cs_generate_variant:Nn \__atableau_add_ribbon:n {V} +\cs_generate_variant:Nn \__atableau_count_row:n {x} +\cs_generate_variant:Nn \__atableau_draw_tableau:n {V} +\cs_generate_variant:Nn \__atableau_entry:n {x} +\cs_generate_variant:Nn \__atableau_draw_abacus_end:nnn {noo} +\cs_generate_variant:Nn \__atableau_set_bead_coordinates:nnn {nVV, nnV, noo, non, noV} +\cs_generate_variant:Nn \__atableau_set_box_coordinates:nnn {nVV, nVn, noo } +\cs_generate_variant:Nn \__atableau_set_partition:nn {no} +\cs_generate_variant:Nn \__atableau_tl_put_right_braced:Nn { NV, Nx, No, Ne } + +% --------------------------------------------------------------------------- +% utility functions + +% usage: \__atableau_tl_put_right_braced:Nn #1 #2 +% Add braces around #2 and append to #1 +\cs_new_protected:Nn \__atableau_tl_put_right_braced:Nn +{ + \tl_put_right:Nn #1 { {#2} } +} + +% --------------------------------------------------------------------------- +% expandable residue functions + +% usage: \__atableau_residue_A:nn +% Computes the residue of #1 mod #2. This is positive integer remainder when +% dividing by #2. As with \int_mod:n, the result is left in the input stream. +\cs_new:Npn \__atableau_residue_A:nn #1 #2 +{ + \int_compare:nNnTF {#1} > {0} + { \int_mod:nn {#1} {#2} } + { \int_eval:n {\int_mod:nn {#2 + \int_mod:nn {#1} {#2}} {#2}} }% \int_mod:nn is negative +} + +% usage: \__atableau_residue_C:nn +% Computes the residue of #1 in affine type C. When e=3 this residue pattern is +% 0 1 2 3 2 1 0 1 2 3 2 1 0 1 ... +% As with \int_mod:n, the result is left in the input stream. +\cs_new:Npn \__atableau_residue_C:nn #1 #2 +{ + \int_compare:nNnTF {#1} < {0} + { % #1 is negative + \int_compare:nNnTF {\int_mod:nn {#1} {2*#2}} < {-#2} + { \int_eval:n { 2*#2 + \int_mod:nn {#1} {2*#2} } } + { \int_eval:n { \int_mod:nn {-#1} {2*#2} } } + } + { % #1 is non-negative + \int_compare:nNnTF {\int_mod:nn {#1} {2*#2}} > {#2} + { \int_eval:n { 2*#2 - \int_mod:nn {#1} {2*#2} } } + { \int_eval:n { \int_mod:nn {#1} {2*#2} } } + } +} + +% usage: \__atableau_residue_AA:nn +% Computes the residue of #1 in twisted type A. When e=3 this residue pattern is +% 0 1 2 3 3 2 1 0 1 2 3 3 2 1 0 1 ... +% As with \int_mod:n, the result is left in the input stream. +\cs_new:Npn \__atableau_residue_AA:nn #1 #2 +{ + \int_compare:nNnTF {#1} < {0} + { % #1 is negative + \int_compare:nNnTF {\int_mod:nn {#1} {2*#2+1}} < {-#2} + { \int_eval:n { 2*#2+1 + \int_mod:nn {#1} {2*#2+1}} } + { \int_eval:n { \int_mod:nn {-#1} {2*#2+1} } } + } + { % #1 is non-negative + \int_compare:nNnTF {\int_mod:nn {#1} {2*#2+1}} > {#2} + { \int_eval:n { 2*#2+1 - \int_mod:nn {#1} {2*#2+1} } } + { \int_eval:n { \int_mod:nn {#1} {2*#2+1} } } + } +} + +% usage: \__atableau_residue_D:nn +% Computes the residue of #1 in affine type C. When e=3 this residue pattern is +% 0 1 2 3 3 2 1 0 0 1 2 3 3 2 1 0 0 1 ... +% As with \int_mod:n, the result is left in the input stream. +\cs_new:Npn \__atableau_residue_DD:nn #1 #2 +{ + \int_compare:nNnTF {#1} < {0} + { % #1 is negative + \int_compare:nNnTF {\int_mod:nn {#1} {2*#2+2}} < {-#2} + { \int_eval:n {2*#2+1 + \int_mod:nn {#1} {2*#2+2}} } + { \int_eval:n { -\int_mod:nn {#1} {2*#2+2} } } + } + { % #1 is non-negative + \int_compare:nNnTF {\int_mod:nn {#1} {2*#2+2}} > {#2} + { \int_eval:n {2*#2+1- \int_mod:nn {#1} {2*#2+2} } } + { \int_eval:n { \int_mod:nn {#1} {2*#2+2} } } + } +} + + +% use type A residues by default +\cs_set_eq:NN \__atableau_residue:nn \__atableau_residue_A:nn + +% --------------------------------------------------------------------------- +% Parsing input + +% \seq_set_split:Nnn does not respect braces around singleton entries +% such as in { 1345, 8{10}, {11}, {12} }, with the result that {11} +% and {12} are treated as {1}{1} and {1}{2}, respectively. As this is +% not what we want, we use \peek_charcode:NTF to do this ourselves. +\cs_new_protected:Npn \__atableau_peek_tableau:w +{ + \peek_remove_spaces:n { % ignore spaces + \peek_charcode_remove:NTF , + { + % record the column index in the shape for drawing the border + \seq_put_right:NV \l__atableau_shape_seq \l__atableau_col_int + + % increment the row index + \int_incr:N \l__atableau_row_int + + % reset the column index, and update the skew shape for shifted tableau + \bool_if:NTF \l__atableau_shifted_bool + { + \seq_put_right:No \l__atableau_skew_seq {\int_use:N \l__atableau_row_int} + \int_set_eq:NN \l__atableau_col_int \l__atableau_row_int + + } + { + \int_set:Nn \l__atableau_col_int { 0\seq_item:Nn \l__atableau_skew_seq {\l__atableau_row_int+1}} + } + + % look for the next entry + \__atableau_peek_tableau:w + } + { + \__atableau_peek_style:nw {draw_entry:nn} + } + } +} + +% --------------------------------------------------------------------------- + +% usage: \__atableau_peek_style:nw {command suffix} +% Read the next entry in the input sequence, with any optional style given by +% a * or [...], and then pass this data to the command \__atableau_#1. +\cs_new_protected:Npn \__atableau_peek_style:nw #1 +{ + \peek_remove_spaces:n { % ignore spaces + \peek_charcode:NTF [ + { \use:c{__atableau_#1} } + { + \peek_charcode_remove:NTF * + { \use:c{__atableau_#1} [\l__atableau_starstyle_tl] } + { \use:c{__atableau_#1} [] } + } + } +} + +% To parse the bead specifications used by \Abacus we need to accept the +% a "bead specification" of the following form +% [style]m^k_txt +% where the [style] could simply be a *, the m is an integer, a part of the +% partition being constructed, the k its exponent and txt is the text for the +% node. Except for m, all of of these components are optional, with the +% exponent being 1 if it is omitted. As is customary, the order of the +% superscript and subscript is interchangeable. To parse these expressions we +% first use \__atableau_peek_style:nw to strip off the style specification, via +% \__atableau_record_style:nn, and then pass between \__atableau_peek_beads:nw +% and \__atableau_record:nn to look ahead for the characters ^ and _ to decide +% which of the following token lists the next character is added to: +\cs_new_protected:Npn \__atableau_record_style:nn [#1] #2 +{ + % clear the bead token lists + \tl_set:Nn \l__atableau_capture_style_tl {#1} + \tl_set:Nn \l__atableau_capture_part_tl {#2} + \tl_clear:N \l__atableau_capture_exp_tl + \tl_clear:N \l__atableau_capture_txt_tl + \__atableau_peek_beads:nw {part} +} + +% look for a caret or an underscore and pass on to \__atableau_record_bead:nn +\cs_new_protected:Npn \__atableau_peek_beads:nw #1 +{ + \peek_remove_spaces:n { % ignore spaces + \peek_charcode_remove:NTF _ + { \__atableau_record_bead:nn {txt} {}} + { + \peek_charcode_remove:NTF ^ { \__atableau_record_bead:nn {exp} {}} + { \__atableau_record_bead:nn {#1} } + } + } +} + +% #1 is one of part, exp or txt +\cs_new_protected:Npn \__atableau_record_bead:nn #1 #2 +{ + \quark_if_recursion_tail_stop_do:nn {#2} + { + % the data in the sequences \l__atableau_capture_exp_tl times + \tl_if_empty:NT \l__atableau_capture_exp_tl { \tl_set:Nn \l__atableau_capture_exp_tl {1}} + \int_step_inline:nn { \l__atableau_capture_exp_tl } + { + \int_incr:N \l__atableau_beads_int + \seq_put_right:NV \l__atableau_shape_seq \l__atableau_capture_part_tl + \seq_put_right:NV \l__atableau_texts_seq \l__atableau_capture_txt_tl + \seq_put_right:NV \l__atableau_styles_seq \l__atableau_capture_style_tl + } + } + \tl_put_right:cn {l__atableau_capture_#1_tl} {#2} + \__atableau_peek_beads:nw {#1} +} + + +% usage: \__atableau_count_entries:nn [style] {entry} +% Count the number of entries in the row, storing the result in c_int +\cs_new_protected:Npn \__atableau_count_entries:nn [#1] #2 +{ + % exit when we reach the end of the row + \quark_if_recursion_tail_stop:n {#2} + + \int_incr:N \l__atableau_c_int + \__atableau_peek_style:nw {count_entries:nn} +} + +% usage: \__atableau_count_row:n {entries} +\cs_new_protected:Npn \__atableau_count_row:n #1 +{ + \__atableau_peek_style:nw {count_entries:nn} #1 \q_recursion_tail \q_recursion_stop +} + +% --------------------------------------------------------------------------- +% creating custom diagrams + +% usage: \__atableau_compute_conjugate_partition:N {seq} +% compute the conjugate partition of <seq> and store in \l__atableau_conjugate_seq +\cs_new_protected:Npn \__atableau_compute_conjugate_partition:N #1 +{ + \seq_clear:N \l__atableau_conjugate_seq + \int_zero:N \l_tmpa_int % previous part + \int_set:No \l__atableau_r_int {\seq_count:N #1} + \int_while_do:nn {\l__atableau_r_int > 0} + { + \int_set:No \l_tmpb_int {\seq_item:NV #1 \l__atableau_r_int } + \int_step_inline:nn { \l_tmpb_int - \l_tmpa_int } + { + \seq_put_right:NV \l__atableau_conjugate_seq \l__atableau_r_int + } + \int_set_eq:NN \l_tmpa_int \l_tmpb_int + \int_decr:N \l__atableau_r_int + } +} + +% usage: \__atableau_set_partition:nn {shape|skew} {csv for partition} +% This function allows the partition to be given either as a comma separated +% list of integers, or as a comma separated list of integers with exponents +% giving the multiplicity of each part. For example, 4,3,3,3,2 and 4,3^3,2 +% are both supported. +% TODO? Rewrite using quarks? +\cs_new_protected:Npn \__atableau_set_partition:nn #1 #2 +{ + \seq_clear:c {l__atableau_#1_seq} + \clist_map_inline:nn {#2} { \__atableau_add_to_partition:nn {#1} {##1} } +} + +% add parts to the partition \l__atableau_#1_seq given input +% of the form k or k^r +\cs_new_protected:Npn \__atableau_add_to_partition:nn #1 #2 +{ + % split #1 on ^: the trailing ^1 sets the exponent to 1 if it's omitted + \seq_set_split:Nnn \l_tmpa_seq {^} {#2 ^1} + % given k or k^r, set \l_tmpa_int=k and \l_tmpb_int=r, where r=1 with input k + \seq_pop_left:NN \l_tmpa_seq \l_tmpa_tl % part + \seq_pop_left:NN \l_tmpa_seq \l_tmpb_tl % exponent + % now add \l_tmpa_int to \l__atableau_#1_seq b times + \int_step_inline:nn {\l_tmpb_tl} { \seq_put_right:ce {l__atableau_#1_seq} {\l_tmpa_tl} } +} + +% usage: \__atableau_diagram_for_shape:N {partition} +% Young diagrams are drawn using the \Tableau command by generating +% a sequence of ~ for the partition. For example, +% it replaces the sequence 3,2,2,1 with +% the dot-sequence ~~~,~~,~~,~, +\cs_new_protected:Npn \__atableau_diagram_for_shape:N #1 +{ + \tl_clear:N \l__atableau_entries_tl + \tl_set:Nn \l_tmpb_tl {} + \seq_map_inline:Nn #1 + { + \tl_put_right:No \l__atableau_entries_tl \l_tmpb_tl + \tl_put_right:Nx \l__atableau_entries_tl { \prg_replicate:nn {##1} {{{~}}} } + \tl_set:Nn \l_tmpb_tl {,} + } +} + +% usage: \__atableau_shape_to_content:Nn {partition sequence} +% Return the content sequence to for a tableau of this shape +\cs_new_protected:Npn \__atableau_shape_to_content:N #1 +{ + \tl_clear:N \l__atableau_entries_tl + \int_zero:N \l__atableau_row_int + \tl_set:Nn \l_tmpb_tl {} + \seq_map_inline:Nn #1 + { + \tl_put_right:No \l__atableau_entries_tl \l_tmpb_tl + \int_incr:N \l__atableau_row_int + \int_step_inline:nn {##1} + { + \int_set:Nn \l_tmpa_int { \l__atableau_charge_int + ####1 - \l__atableau_row_int + 0\seq_item:NV \l__atableau_skew_seq \l__atableau_row_int } + \int_compare:nNnTF {\l_tmpa_int} < {0} + { \__atableau_tl_put_right_braced:Ne \l__atableau_entries_tl {\exp_not:N\shortminus\int_eval:n{-\l_tmpa_int}} } + { \__atableau_tl_put_right_braced:No \l__atableau_entries_tl {\int_use:N \l_tmpa_int} } + } + \tl_set:Nn \l_tmpb_tl {,} + } +} + +% Diagrams with show=last are drawn using the \Tableau command, with +% usage: \__atableau_shape_to_last:N {partition sequence} +\cs_new_protected:Npn \__atableau_shape_to_last:N #1 +{ + % compute conjugate of #1 as \l__atableau_conjugate_seq + %\__atableau_compute_conjugate_partition:N #1 + + % initialise \l_tmpc_seq -- should not be necessary but otherwise \seq_item:NV fails + \seq_clear:N \l_tmpc_seq % construct the tableau in this sequence and then decant + \seq_map_inline:Nn #1 {\seq_put_right:Nn \l_tmpc_seq {}} + + % value of last entry added to the tableau + \int_set:Nn \l__atableau_c_int {\l__atableau_charge_int} + + % length of the first row of the partition/seq #1 + \int_set:Nn \l__atableau_row_int {0\seq_item:Nn #1 {1}} + + \int_step_inline:nn {\l__atableau_row_int} % ##1 is the column index + { + \int_zero:N \l__atableau_r_int % r_int is the row index + \seq_map_inline:Nn #1 % ####1 is the rth entry of the partition #1 + { + \int_incr:N \l__atableau_r_int + \tl_set:Nn \l_tmpa_tl { 0\seq_item:NV \l__atableau_skew_seq \l__atableau_r_int} + \bool_if:nT { \int_compare_p:nNn {##1} > {\l_tmpa_tl} && \int_compare_p:nNn {\l_tmpa_tl+####1+1} > {##1}} + { + \int_incr:N \l__atableau_c_int + \tl_set:No \l_tmpa_tl { \seq_item:Nn \l_tmpc_seq {\l__atableau_r_int} } + \__atableau_tl_put_right_braced:No \l_tmpa_tl {\int_use:N \l__atableau_c_int} + \seq_set_item:NVx \l_tmpc_seq \l__atableau_r_int {\l_tmpa_tl} + } + } + } + + % finally, unpack \l_tmpc_tl into l__atableau_entries_tl + \tl_clear:N \l__atableau_entries_tl + \tl_set:Nn \l_tmpb_tl {} + \seq_map_inline:Nn \l_tmpc_seq { + \tl_put_right:No \l__atableau_entries_tl \l_tmpb_tl + \tl_put_right:Nn \l__atableau_entries_tl {##1} + \tl_set:Nn \l_tmpb_tl {,} + } +} + +% Diagrams with show=first are drawn using the \Tableau command, with +% usage: \__atableau_shape_to_first:N {partition sequence} +\cs_new_protected:Npn \__atableau_shape_to_first:N #1 +{ + \tl_clear:N \l__atableau_entries_tl + \int_set:Nn \l__atableau_r_int {\l__atableau_charge_int} + \tl_set:Nn \l_tmpb_tl {} + \seq_map_inline:Nn #1 + { + \tl_put_right:No \l__atableau_entries_tl \l_tmpb_tl + \int_step_inline:nn {##1} + { + \int_incr:N \l__atableau_r_int + \__atableau_tl_put_right_braced:NV \l__atableau_entries_tl \l__atableau_r_int + } + \tl_set:Nn \l_tmpb_tl {,} + } + \seq_put_right:No \l__atableau_charge_seq {\int_use:N\l__atableau_r_int} +} + +% usage: \__atableau_shape_to_hook:N {partition_seq} +% Return a string for the hooks length tableau +% Diagrams with show=hooks are drawn using the \Tableau command +\cs_new_protected:Npn \__atableau_shape_to_hook:N #1 +{ + % now construct the hook tableau + \tl_clear:N \l__atableau_entries_tl + \bool_if:NTF \l__atableau_shifted_bool + { % shifted tableau + % initialise l_tmpc_seq because we will construct the entries in it and unpack at the end + \seq_clear:N \l_tmpc_seq + \seq_map_inline:Nn #1 {\seq_put_right:Nn \l_tmpc_seq {}} + \int_set:Nn \l__atableau_col_int { 0\seq_item:Nn #1 {1} } % length of first row + \int_step_inline:nn { \l__atableau_col_int } + { % add hooks column by column, starting by determining the rows in columns ##1 + \int_zero:N \l__atableau_row_int + \seq_map_inline:Nn #1 + { + \bool_if:nT { \int_compare_p:nNn {##1} > {\l__atableau_row_int} && \int_compare_p:nNn {####1+\l__atableau_row_int+1} > {##1} } + { + \int_incr:N \l__atableau_row_int + } + } + % record length of row col+1 + \int_set:Nn \l_tmpa_int {0\seq_item:Nn #1 {##1+1}} + % loop through rows 1,...,row and add the hook lengths + \int_step_inline:nn {\l__atableau_row_int} + { + \tl_set:Nx \l_tmpa_tl { \seq_item:Nn \l_tmpc_seq {####1} } + \tl_set:Nx \l_tmpb_tl { \int_eval:n {\seq_item:Nn #1 {####1} + \l__atableau_row_int - ##1 + \l_tmpa_int } } + \__atableau_tl_put_right_braced:NV \l_tmpa_tl \l_tmpb_tl + \seq_set_item:Nnx \l_tmpc_seq {####1} {\l_tmpa_tl} + + } + } + \tl_set:Nx \l__atableau_entries_tl { \seq_use:Nn \l_tmpc_seq {,} } + } + { % unshifted (although could be skew, when all bets are off...) + % compute conjugate of #1 as \l__atableau_conjugate_seq + \int_zero:N \l__atableau_row_int + \tl_set:Nn \l_tmpb_tl {} + \__atableau_compute_conjugate_partition:N #1 + \seq_map_inline:Nn #1 + { + \tl_put_right:No \l__atableau_entries_tl \l_tmpb_tl + \int_incr:N \l__atableau_row_int + \int_step_inline:nn {##1} + { + \tl_set:Ne \l_tmpc_tl { \int_eval:n {##1+\seq_item:Nn \l__atableau_conjugate_seq {####1} -\l__atableau_row_int-####1+1} } + \__atableau_tl_put_right_braced:NV \l__atableau_entries_tl \l_tmpc_tl + } + \tl_set:Nn \l_tmpb_tl {,} + } + } +} + +% Residue diagrams are drawn using the \Tableau command, with +% usage: \__atableau_shape_to_residue:N {partition sequence} +\cs_new_protected:Npn \__atableau_shape_to_residue:N #1 +{ + \tl_clear:N \l__atableau_entries_tl + \int_zero:N \l__atableau_row_int + \tl_set:Nn \l_tmpb_tl {} + \seq_map_inline:Nn #1 + { + \tl_put_right:No \l__atableau_entries_tl \l_tmpb_tl + \int_incr:N \l__atableau_row_int + \int_step_inline:nn {##1} + { + \int_set:Nn \l_tmpa_int { \l__atableau_charge_int + ####1 - \l__atableau_row_int } + \int_add:Nn \l_tmpa_int { + 0\seq_item:NV \l__atableau_skew_seq \l__atableau_row_int } + \tl_set:Nx \l_tmpa_tl { \__atableau_residue:nn { \l_tmpa_int } {\l__atableau_e_int} } + \__atableau_tl_put_right_braced:Nx \l__atableau_entries_tl { \l_tmpa_tl } + } + \tl_set:Nn \l_tmpb_tl {,} + } +} + +% --------------------------------------------------------------------------- +% draw diagram border + +% usage: \__atableau_draw_border:nn #1 #2 {name of sequence} {name of style} +% The name of the sequence is either skew or shape +\cs_new_protected:Npn \__atableau_draw_border:nn #1 #2 +{ + \int_zero:N \l__atableau_r_int % row index + \int_zero:N \l__atableau_c_int % column index + \tl_clear:N \l__atableau_border_tl % will hold the border + + \seq_map_inline:cn {l__atableau_#1_seq} + { + % compute the endpoints of this row + \str_if_eq:nnF {skew} {#1} + { + \int_set:Nn \l__atableau_c_int { 0\seq_item:Nn \l__atableau_skew_seq {\l__atableau_r_int+1}} + \bool_if:nT { \l__atableau_tabloid_bool && \int_compare_p:nNn {\l__atableau_r_int} > {0} } + { + \int_set:Nn \l__atableau_c_int { \int_min:nn {\l__atableau_c_int} {0\seq_item:Nn \l__atableau_skew_seq {\l__atableau_r_int}} } + } + } + \__atableau_set_box_coordinates:nVV {a} \l__atableau_r_int \l__atableau_c_int + \__atableau_set_box_coordinates:nVn {b} \l__atableau_r_int {##1} + \bool_if:nTF { \int_compare_p:n {\l__atableau_r_int = 0} || \l__atableau_tabloid_bool } + { + % add line along "top" of the row + \tl_put_right:Nx \l__atableau_border_tl {(\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)--(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp)} + \__atableau_add_row_ends: + } + { + % add lines for second and later rows + \tl_put_left:Nx \l__atableau_border_tl {(\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)--} + \tl_put_right:Nx \l__atableau_border_tl {--(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp)} + \__atableau_add_row_ends: + } + \int_incr:N \l__atableau_r_int + } + + % draw the border that we have constructed + \tl_if_empty:NF \l__atableau_border_tl + { + % fill in the last line + \bool_if:nT { \l__atableau_tabloid_bool && \int_compare_p:nNn {\l__atableau_r_int} > {0} } + { + \int_set:Nn \l__atableau_c_int { 0\seq_item:NV \l__atableau_skew_seq \l__atableau_r_int } + \__atableau_set_box_coordinates:nVV {a} \l__atableau_r_int \l__atableau_c_int + } + \tl_put_right:Nx \l__atableau_border_tl {(\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)--(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp)} + % draw the border + \draw[aTableau/#2] \l__atableau_border_tl; + } +} + + +% usage: \__atableau_remove_dotted_tableau_rows: +% Poke some holes in the border for the rows in dotted_rows_seq +\cs_new_protected:Nn \__atableau_remove_dotted_tableau_rows: +{ + % To collect repeated rows in dotted_rows_seq + % we use \seq_map_inline:Nn and then compare ##1 with \l__atableau_r_int to + % determine if this is a new row. + + % take a copy of \l__atableau_dotted_rows_seq so that the pop_left's below + % do not destroy it + \seq_set_eq:NN \l_tmpb_seq \l__atableau_dotted_rows_seq + \bool_do_until:nn { \seq_if_empty_p:N \l_tmpb_seq } + { + \seq_pop_left:NN \l_tmpb_seq \l_tmpa_tl + \int_set:Nn \l__atableau_row_int {\l_tmpa_tl} + \int_set:Nn \l__atableau_r_int {\l__atableau_row_int+1} + + % LaTeX3 does not provide \seq_if_in_p:NN, so ... + \bool_set_true:N \l_tmpa_bool + \bool_do_while:nn { \l_tmpa_bool } + { + \int_set:Nn \l_tmpa_int {0\seq_item:Nn \l_tmpb_seq 1} + \int_compare:nNnTF {\l__atableau_r_int} = {\l_tmpa_int} + { + \seq_pop_left:NN \l_tmpb_seq \l_tmpa_tl + \int_incr:N \l__atableau_r_int + } + { \bool_set_false:N \l_tmpa_bool } + } + + % We want to blank out the rows between the four coordinates + % a=(row,tmpa) .... b=(row,col) + % | | + % c=(r,tmpb) .... d=(r,c) + + % set tmpa and tmpb to the column index for rows row and r, respectively + \int_set:No \l__atableau_col_int { 0\seq_item:NV \l__atableau_shape_seq \l__atableau_row_int } % mu_row + \int_set:No \l__atableau_c_int { 0\seq_item:NV \l__atableau_shape_seq \l__atableau_r_int } % mu_r + + % shift in col-direction + \fp_set:Nn \l__atableau_xa_fp {\l__atableau_tab_col_dx_fp*\l__atableau_box_wd_fp} + \fp_set:Nn \l__atableau_ya_fp {\l__atableau_tab_col_dy_fp*\l__atableau_box_ht_fp} + + % shift in row-direction + \fp_set:Nn \l__atableau_xb_fp {\l__atableau_tab_row_dx_fp*\l__atableau_box_wd_fp} + \fp_set:Nn \l__atableau_yb_fp {\l__atableau_tab_row_dy_fp*\l__atableau_box_ht_fp} + + % a draw between the coordinates a, b, c, d + \__atableau_set_box_coordinates:noo {l} {\l__atableau_row_int-1} {\l__atableau_col_int-1} % point b + + \draw[aTableau/clearBoxes] + (\fp_eval:n{\l__atableau_xl_fp+0.58*\l__atableau_xa_fp-0.42*\l__atableau_xb_fp}, + \fp_eval:n{\l__atableau_yl_fp+0.58*\l__atableau_ya_fp-0.42*\l__atableau_yb_fp}) + --++(\fp_eval:n{(\l__atableau_r_int-\l__atableau_row_int)*\l__atableau_xb_fp}, + \fp_eval:n{(\l__atableau_r_int-\l__atableau_row_int)*\l__atableau_yb_fp}) + --++(\fp_eval:n{(\l__atableau_c_int-\l__atableau_col_int)*\l__atableau_xa_fp}, + \fp_eval:n{(\l__atableau_c_int-\l__atableau_col_int)*\l__atableau_ya_fp}) + --++(\fp_eval:n{-0.12*\l__atableau_xb_fp}, \fp_eval:n{-0.12*\l__atableau_yb_fp}) + --++(\fp_eval:n{-(0.15+\l__atableau_c_int)*\l__atableau_xa_fp}, \fp_eval:n{-(0.15+\l__atableau_c_int)*\l__atableau_ya_fp}) + --++(\fp_eval:n{(\l__atableau_row_int-\l__atableau_r_int+0.12)*\l__atableau_xb_fp}, + \fp_eval:n{(\l__atableau_row_int-\l__atableau_r_int+0.12)*\l__atableau_yb_fp}) + --cycle + ; + + % finally, we need to add dots between b and d + \draw[aTableau/dottedLine] + (\fp_eval:n{0.5*\l__atableau_xa_fp-0.5*\l__atableau_xb_fp+\l__atableau_xl_fp}, + \fp_eval:n{0.5*\l__atableau_ya_fp-0.5*\l__atableau_yb_fp+\l__atableau_yl_fp}) + --++(\fp_eval:n{(\l__atableau_c_int-\l__atableau_col_int)*\l__atableau_xa_fp+(\l__atableau_r_int-\l__atableau_row_int)*\l__atableau_xb_fp}, + \fp_eval:n{(\l__atableau_c_int-\l__atableau_col_int)*\l__atableau_ya_fp+(\l__atableau_r_int-\l__atableau_row_int)*\l__atableau_yb_fp}) + ; + + % and between a and c, which is trickier as the skew shape plays a role + \int_set:No \l_tmpa_int { \int_eval:n {0\seq_item:Nn \l__atableau_skew_seq \l__atableau_row_int} } + \int_set:No \l_tmpb_int { \int_eval:n {\int_max:nn{\l_tmpa_int}{0\seq_item:Nn \l__atableau_skew_seq \l__atableau_r_int} }} + \int_decr:N \l__atableau_row_int + \int_decr:N \l__atableau_r_int + \draw[aTableau/dottedLine] + (\fp_eval:n{\l__atableau_x_fp+\l_tmpa_int*\l__atableau_xa_fp+\l__atableau_row_int*\l__atableau_xb_fp}, + \fp_eval:n{\l__atableau_y_fp+\l_tmpa_int*\l__atableau_ya_fp+\l__atableau_row_int*\l__atableau_yb_fp}) + --(\fp_eval:n{\l__atableau_x_fp+\l_tmpb_int*\l__atableau_xa_fp+\l__atableau_r_int*\l__atableau_xb_fp}, + \fp_eval:n{\l__atableau_y_fp+\l_tmpb_int*\l__atableau_ya_fp+\l__atableau_r_int*\l__atableau_yb_fp}) + ; + + \bool_if:nT { \int_compare_p:nNn {\l_tmpa_int}>{0} && \l__atableau_skew_border_bool } + { + \draw[aTableau/dottedLine,draw=\l__atableau_skew_border_tl] + (\fp_eval:n{\l__atableau_x_fp+\l__atableau_row_int*\l__atableau_xb_fp}, + \fp_eval:n{\l__atableau_y_fp+\l__atableau_row_int*\l__atableau_yb_fp}) + --(\fp_eval:n{\l__atableau_x_fp+\l__atableau_r_int*\l__atableau_xb_fp}, + \fp_eval:n{\l__atableau_y_fp+\l__atableau_r_int*\l__atableau_yb_fp}) + ; + } + } +} + +% usage: \__atableau_remove_dotted_tableau_cols: +% Poke some holes in the border for the cols in dotted_rows_seq +\cs_new_protected:Nn \__atableau_remove_dotted_tableau_cols: +{ + % To collect repeated columns in dotted_cols_seq + % we use \seq_map_inline:Nn and then compare ##1 with \l__atableau_c_int to + % determine if this is a new column. + + % conjugate partition and skew shape + \__atableau_compute_conjugate_partition:N \l__atableau_skew_seq + \seq_set_eq:NN \l_tmpa_seq \l__atableau_conjugate_seq % \l_tmpa_seq is the conjugate skew + \__atableau_compute_conjugate_partition:N \l__atableau_shape_seq % conjugate shape + + % take a copy of \l__atableau_dotted_cols_seq so that the pop_left's below + % do not destroy it + \seq_set_eq:NN \l_tmpb_seq \l__atableau_dotted_cols_seq + \bool_do_until:nn { \seq_if_empty_p:N \l_tmpb_seq } + { + \seq_pop_left:NN \l_tmpb_seq \l_tmpa_tl + \int_set:Nn \l__atableau_col_int {\l_tmpa_tl} + \int_set:Nn \l__atableau_c_int {\l__atableau_col_int+1} + + % LaTeX3 does not provide \seq_if_in_p:NN, so ... + \bool_set_true:N \l_tmpa_bool + \bool_do_while:nn { \l_tmpa_bool } + { + \int_set:Nn \l_tmpa_int {0\seq_item:Nn \l_tmpb_seq 1} + \int_compare:nNnTF {\l__atableau_c_int} = {\l_tmpa_int} + { + \seq_pop_left:NN \l_tmpb_seq \l_tmpa_tl + \int_incr:N \l__atableau_c_int + } + { \bool_set_false:N \l_tmpa_bool } + } + + % We want to blank out the rows between the four coordinates + % a=(row,tmpa) .... b=(r,tmpb) + % | | + % c=(row,col) .... d=(r,c) + + % set row and r to the row indices for col and c, respectively + \int_set:No \l__atableau_row_int { 0\seq_item:NV \l__atableau_conjugate_seq \l__atableau_col_int } % mu_row + \int_set:No \l__atableau_r_int { 0\seq_item:NV \l__atableau_conjugate_seq \l__atableau_c_int } % mu_r + + + % shift in col-direction + \fp_set:Nn \l__atableau_xa_fp {\l__atableau_tab_col_dx_fp*\l__atableau_box_wd_fp} + \fp_set:Nn \l__atableau_ya_fp {\l__atableau_tab_col_dy_fp*\l__atableau_box_ht_fp} + + % shift in row-direction + \fp_set:Nn \l__atableau_xb_fp {\l__atableau_tab_row_dx_fp*\l__atableau_box_wd_fp} + \fp_set:Nn \l__atableau_yb_fp {\l__atableau_tab_row_dy_fp*\l__atableau_box_ht_fp} + + % a draw between the coordinates a, b, c, d + \__atableau_set_box_coordinates:noo {l} {\l__atableau_row_int-1} {\l__atableau_col_int-1} % point b + + \draw[aTableau/clearBoxes] + (\fp_eval:n{\l__atableau_xl_fp-0.42*\l__atableau_xa_fp+0.58*\l__atableau_xb_fp}, + \fp_eval:n{\l__atableau_yl_fp-0.42*\l__atableau_ya_fp+0.58*\l__atableau_yb_fp}) + --++(\fp_eval:n{(\l__atableau_c_int-\l__atableau_col_int)*\l__atableau_xa_fp}, + \fp_eval:n{(\l__atableau_c_int-\l__atableau_col_int)*\l__atableau_ya_fp}) + --++(\fp_eval:n{(\l__atableau_r_int-\l__atableau_row_int)*\l__atableau_xb_fp}, + \fp_eval:n{(\l__atableau_r_int-\l__atableau_row_int)*\l__atableau_yb_fp}) + --++(\fp_eval:n{-0.12*\l__atableau_xa_fp}, \fp_eval:n{-0.12*\l__atableau_ya_fp}) + --++(\fp_eval:n{-(0.15+\l__atableau_r_int)*\l__atableau_xb_fp}, \fp_eval:n{-(0.15+\l__atableau_r_int)*\l__atableau_yb_fp}) + --++(\fp_eval:n{(\l__atableau_col_int-\l__atableau_c_int+0.12)*\l__atableau_xa_fp}, + \fp_eval:n{(\l__atableau_col_int-\l__atableau_c_int+0.12)*\l__atableau_ya_fp}) + --cycle + ; + + % finally, we need to add dots between b and d + \draw[aTableau/dottedLine] + (\fp_eval:n{\l__atableau_xl_fp-0.5*\l__atableau_xa_fp+0.5*\l__atableau_xb_fp}, + \fp_eval:n{\l__atableau_yl_fp-0.5*\l__atableau_ya_fp+0.5*\l__atableau_yb_fp}) + --++(\fp_eval:n{(\l__atableau_r_int-\l__atableau_row_int)*\l__atableau_xb_fp+(\l__atableau_c_int-\l__atableau_col_int)*\l__atableau_xa_fp}, + \fp_eval:n{(\l__atableau_r_int-\l__atableau_row_int)*\l__atableau_yb_fp+(\l__atableau_c_int-\l__atableau_col_int)*\l__atableau_ya_fp}) + ; + + % and between a and c, which is trickier as the skew shape plays a role + \int_set:No \l_tmpa_int { \int_eval:n {0\seq_item:Nn \l_tmpa_seq \l__atableau_col_int} } + \int_set:No \l_tmpb_int { \int_eval:n {\int_max:nn{\l_tmpa_int}{0\seq_item:Nn \l_tmpa_seq \l__atableau_c_int} }} + \int_decr:N \l__atableau_col_int + \int_decr:N \l__atableau_c_int + \draw[aTableau/dottedLine] + (\fp_eval:n{\l__atableau_x_fp+\l_tmpa_int*\l__atableau_xb_fp+\l__atableau_col_int*\l__atableau_xa_fp}, + \fp_eval:n{\l__atableau_y_fp+\l_tmpa_int*\l__atableau_yb_fp+\l__atableau_col_int*\l__atableau_ya_fp}) + --(\fp_eval:n{\l__atableau_x_fp+\l_tmpb_int*\l__atableau_xb_fp+\l__atableau_c_int*\l__atableau_xa_fp}, + \fp_eval:n{\l__atableau_y_fp+\l_tmpb_int*\l__atableau_yb_fp+\l__atableau_c_int*\l__atableau_ya_fp}) + ; + + \bool_if:nT { \int_compare_p:nNn {\l_tmpa_int}>{0} && \l__atableau_skew_border_bool } + { + \draw[aTableau/dottedLine,draw=\l__atableau_skew_border_tl] + (\fp_eval:n{\l__atableau_x_fp+\l__atableau_col_int*\l__atableau_xa_fp}, + \fp_eval:n{\l__atableau_y_fp+\l__atableau_col_int*\l__atableau_ya_fp}) + --(\fp_eval:n{\l__atableau_x_fp+\l__atableau_c_int*\l__atableau_xa_fp}, + \fp_eval:n{\l__atableau_y_fp+\l__atableau_c_int*\l__atableau_ya_fp}) + ; + } + } +} + +% usage: \__atableau_draw_label: +% Add the label to a diagram +\cs_new_protected:Nn \__atableau_draw_label: +{ + % determine where the label should be attached, which is the (1,1)-box by default + \fp_set_eq:NN \l__atableau_xa_fp \l__atableau_x_fp + \fp_set_eq:NN \l__atableau_ya_fp \l__atableau_y_fp + \bool_if:nF { \seq_if_empty_p:N \l__atableau_skew_seq || \l__atableau_skew_border_bool } + { % attach label to the (1,skew_1)-box + \tl_set:Nn \l_tmpa_tl { 0\seq_item:Nn \l__atableau_skew_seq {1} } + \fp_add:Nn \l__atableau_xa_fp {\l_tmpa_tl*\l__atableau_tab_col_dx_fp*\l__atableau_box_wd_fp} + \fp_add:Nn \l__atableau_ya_fp {\l_tmpa_tl*\l__atableau_tab_col_dy_fp*\l__atableau_box_wd_fp} + } + % add the label + \node[aTableau/labelStyle] at (\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp) { \__atableau_entry:n{\l__atableau_label_tl} }; +} + +% usage: \__atableau_draw_skew_boxes: +% Draw the skew boxes using ribbons +\cs_new_protected:Nn \__atableau_draw_skew_boxes: +{ + \group_begin: + % override the style of the skew boxes inside this group + \tikzset{aTableau/ribbonBox/.style=aTableau/skewBox} + \tikzset{aTableau/ribbonStyle/.style={draw=none,fill=none}} + \tl_set:Nn \l__atableau_ribbon_type_tl {ribbon} % change ribbon type to ribbon + \int_zero:N \l__atableau_r_int + \seq_map_inline:Nn \l__atableau_skew_seq + { + \int_incr:N \l__atableau_r_int + \int_compare:nNnT {##1} > {0} + { + \tl_clear:N \l_tmpa_tl + \tl_put_right:Ne \l_tmpa_tl { {\int_use:N \l__atableau_r_int} } + \tl_put_right:Ne \l_tmpa_tl { {##1} } + \tl_put_right:Ne \l_tmpa_tl { \prg_replicate:nn {##1-1} {c} } + \__atableau_add_ribbon:V \l_tmpa_tl + } + } + \group_end: +} + +% usage: \__atableau_add_row_ends: increment the (xa,ya) and (xb,yb) coordinates down +% one row and add the lines at the left and right hand ends of the row to +% \l__atableau_border_tl. If this is a tabloid then we only want to add the +% coordinates but otherwise we join them up +\cs_new_protected:Nn \__atableau_add_row_ends: +{ + \bool_if:NTF \l__atableau_conjugate_bool + { + % adding to the left-hand side + \fp_add:Nn \l__atableau_xa_fp {\l__atableau_tab_col_dx_fp*\l__atableau_box_wd_fp} + \fp_add:Nn \l__atableau_ya_fp {\l__atableau_tab_col_dy_fp*\l__atableau_box_ht_fp} + + % adding to the right-hand side + \fp_add:Nn \l__atableau_xb_fp {\l__atableau_tab_col_dx_fp*\l__atableau_box_wd_fp} + \fp_add:Nn \l__atableau_yb_fp {\l__atableau_tab_col_dy_fp*\l__atableau_box_ht_fp} + + \bool_if:NTF \l__atableau_tabloid_bool + { + \tl_put_left:Nx \l__atableau_border_tl {--(\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)} + \tl_put_right:Nx \l__atableau_border_tl {(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp)--} + } + { + \tl_put_left:Nx \l__atableau_border_tl {(\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)--} + \tl_put_right:Nx \l__atableau_border_tl {--(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp)} + } + } + { + % adding to the left-hand side + \fp_add:Nn \l__atableau_xa_fp {\l__atableau_tab_row_dx_fp*\l__atableau_box_wd_fp} + \fp_add:Nn \l__atableau_ya_fp {\l__atableau_tab_row_dy_fp*\l__atableau_box_ht_fp} + + % adding to the right-hand side + \fp_add:Nn \l__atableau_xb_fp {\l__atableau_tab_row_dx_fp*\l__atableau_box_wd_fp} + \fp_add:Nn \l__atableau_yb_fp {\l__atableau_tab_row_dy_fp*\l__atableau_box_ht_fp} + + \bool_if:NTF \l__atableau_tabloid_bool + { + \tl_put_left:Nx \l__atableau_border_tl {(\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)} + \tl_put_right:Nx \l__atableau_border_tl {(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp)--} + } + { + \tl_put_left:Nx \l__atableau_border_tl {(\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)--} + \tl_put_right:Nx \l__atableau_border_tl {--(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp)} + } + } +} + +% --------------------------------------------------------------------------- +% Tableaux + +% usage: \__atableau_draw_tableau:n {tableau specifications} +% The entries are first primarily because the \Diagram commands need +% to force the entries to expand +\cs_new_protected:Npn \__atableau_draw_tableau:n #1 +{ + % set the star style + \tl_set:Nn \l__atableau_starstyle_tl {aTableau/tableauStarStyle} + + % disable interior boxes if boxes_bool is false + \bool_if:NF \l__atableau_boxes_bool { \__atableau_tikzset_append:nn {boxStyle} {draw=none} } + + % initialise the skew shape for a shifted tableaux + \bool_if:NT \l__atableau_shifted_bool + { + \seq_clear:N \l__atableau_skew_seq + \seq_put_right:No \l__atableau_skew_seq {0} + } + + % record the tableau shape as we go so that we can draw the border + % in finalise_tableau + \seq_clear:N \l__atableau_shape_seq + + % the styled boxes are drawn at the end + \tl_clear:N \l__atableau_styled_nodes_tl + + % set initial row and column + \int_zero:N \l__atableau_row_int + \int_set:Nn \l__atableau_col_int { 0\seq_item:Nn \l__atableau_skew_seq {1}} + + % Parse #1 into the rows and columns with style. Initially we used + % \seq_set_split:Nnn here, but this required double bracing + % multi-character entries whenever they were he only entry in their + % column. Now we peek for the commas and the style simultaneously. + \__atableau_peek_tableau:w #1 \q_recursion_tail \q_recursion_stop + + % add the nodes with style + \l__atableau_styled_nodes_tl + + % add the labels, ribbons, snobs and border + \__atableau_finalise_tableau: +} + +% usage: \__atableau_finalise_tableau: +% As it is used in several places, collect the code that finishes +% drawing the tableau by adding the ribbons, paths, snobs, skew boxes, +% border and the dotted rows and columns. +\cs_new_protected:Nn \__atableau_finalise_tableau: +{ + % prevent paths and ribbons from updating shape + \cs_set_eq:NN \__atableau_update_shape: \prg_do_nothing: + + % add paths + \seq_if_empty:NF \l__atableau_paths_seq + { + \tl_set:Nn \l__atableau_ribbon_type_tl {path} % change ribbon type to path + \seq_map_inline:Nn \l__atableau_paths_seq {\__atableau_add_ribbon:n {##1}} + } + + % add ribbons + \tl_set:Nn \l__atableau_ribbon_type_tl {ribbon} % change ribbon type to ribbon + \seq_map_inline:Nn \l__atableau_ribbons_seq {\__atableau_add_ribbon:n {##1}} + + % paths and ribbons are inside the delimiters, but snobs are not + \cs_set_eq:NN \__atableau_update_extrema:n \use_none:n + + % draw border + \tl_if_blank:VF \l__atableau_label_tl { \__atableau_draw_label: } + \bool_if:NT \l__atableau_skew_boxes_bool { \__atableau_draw_skew_boxes: } + \bool_if:NT \l__atableau_skew_border_bool { \__atableau_draw_border:nn {skew} {skewBorder} } + \bool_if:NT \l__atableau_border_bool { \__atableau_draw_border:nn {shape} {borderStyle} } + + % remove dotted rows and columns + \seq_if_empty:NF \l__atableau_dotted_rows_seq \__atableau_remove_dotted_tableau_rows: + \seq_if_empty:NF \l__atableau_dotted_cols_seq \__atableau_remove_dotted_tableau_cols: + + % add snobs + \tl_set:Nn \l__atableau_ribbon_type_tl {snob} % change ribbon type to snob + \seq_map_inline:Nn \l__atableau_snobs_seq {\__atableau_add_ribbon:n {##1}} +} + +% --------------------------------------------------------------------------- +% box and bead coordinates + +% usage: \__atableau_update_multi_extrema:n {letter} +% Update xmax, ymax and ymin using the x#1_fp and y#1_fp +\cs_new_protected:Npn \__atableau_update_multi_extrema:n #1 +{ + % adjust xmax, ymin and ymax for multishapes + \fp_set:Nn \l__atableau_xmax_fp { max(\l__atableau_xmax_fp, \use:c{l__atableau_x#1_fp}) } + \fp_set:Nn \l__atableau_ymax_fp { max(\l__atableau_ymax_fp, \use:c{l__atableau_y#1_fp}) } + \fp_set:Nn \l__atableau_ymin_fp { min(\l__atableau_ymin_fp, \use:c{l__atableau_y#1_fp}) } +} + +\cs_new_protected:Npn \__atableau_update_multi_xtrema:n #1 +{ + % adjust xmax for multishapes + \fp_set:Nn \l__atableau_xmax_fp { max(\l__atableau_xmax_fp, \use:c{l__atableau_x#1_fp}) } +} + +% By default, we do not update ymin, ymax and xmax. This only happens for multishapes +\cs_set_eq:NN \__atableau_update_extrema:n \use_none:n + +% usage: \__atableau_set_box_coordinates_normal:nnn <letter> <row> <col>: given the row and columns +% indices, row and col, define the corresponding coordinates in a tableau +% - \l__atableau_xl_fp : x-coordinates +% - \l__atableau_yl_fp : y-coordinate +% - \l__atableau_name_tl : the node name +% Used by the tableau and diagram commands. Note that row and col both start +% from 0, so the (1,1)-box has row=col=0. +\cs_new_protected:Npn \__atableau_set_box_coordinates_normal:nnn #1 #2 #3 +{ + \tl_set:Nx \l__atableau_name_tl {\l__atableau_prefix_tl-\int_eval:n{1+#2}-\int_eval:n {1+#3}} + \fp_set:cn {l__atableau_x#1_fp} {\l__atableau_x_fp+((#2+0.5)*\l__atableau_tab_row_dx_fp+(#3+0.5)*\l__atableau_tab_col_dx_fp)*\l__atableau_box_wd_fp } + \fp_set:cn {l__atableau_y#1_fp} {\l__atableau_y_fp+((#2+0.5)*\l__atableau_tab_row_dy_fp+(#3+0.5)*\l__atableau_tab_col_dy_fp)*\l__atableau_box_ht_fp } + \__atableau_update_extrema:n #1 +} + +% usage: \__atableau_set_box_coordinates_conjugate:nnn <letter> <row> <col>: given the row and columns +% indices, row and col, define the corresponding coordinates in the conjugate tableau +% - \l__atableau_xl_fp : x-coordinates +% - \l__atableau_yl_fp : y-coordinate +% - \l__atableau_name_tl : the node name +% Used by the tableau and diagram commands +\cs_new_protected:Npn \__atableau_set_box_coordinates_conjugate:nnn #1 #2 #3 +{ + \tl_set:Nx \l__atableau_name_tl {\l__atableau_prefix_tl-\int_eval:n{1+#3}-\int_eval:n {1+#2}} + \fp_set:cn {l__atableau_x#1_fp} {\l__atableau_x_fp+((#3+0.5)*\l__atableau_tab_row_dx_fp+(#2+0.5)*\l__atableau_tab_col_dx_fp)*\l__atableau_box_wd_fp } + \fp_set:cn {l__atableau_y#1_fp} {\l__atableau_y_fp+((#3+0.5)*\l__atableau_tab_row_dy_fp+(#2+0.5)*\l__atableau_tab_col_dy_fp)*\l__atableau_box_ht_fp } + \__atableau_update_extrema:n #1 +} + +% by default , normal coordinates are used +\cs_set_eq:NN \__atableau_set_box_coordinates:nnn \__atableau_set_box_coordinates_normal:nnn + + +% usage: \__atableau_set_bead_coordinates:nnn <letter> <row> <col>: given the row and columns +% indices, row and col, define the corresponding coordinates in a tableau or abacus: +% - \l__atableau_xl_fp : x-coordinates +% - \l__atableau_yl_fp : y-coordinate +% - \l__atableau_name_tl : the node name +% Used by both the tableaux and abacus commands +\cs_new_protected:Npn \__atableau_set_bead_coordinates:nnn #1 #2 #3 +{ + \tl_set:Nx \l__atableau_name_tl {\l__atableau_prefix_tl-\fp_to_int:n{#2-1}-\fp_to_int:n{#3}} + \fp_set:cn {l__atableau_x#1_fp} {\l__atableau_x_fp+(#2*\l__atableau_ab_row_dx_fp+#3*\l__atableau_ab_col_dx_fp)*\l__atableau_abacus_wd_fp } + \fp_set:cn {l__atableau_y#1_fp} {\l__atableau_y_fp+(#2*\l__atableau_ab_row_dy_fp+#3*\l__atableau_ab_col_dy_fp)*\l__atableau_abacus_ht_fp } +} + +% --------------------------------------------------------------------------- +% tableaux boxes/nodes + +\cs_new_protected:Npn \__atableau_valign_bottom:n #1 { \vbox_to_zero:n { #1 \vss } } +\cs_new_protected:Npn \__atableau_valign_centre:n #1 { \vbox_to_zero:n { \vss #1 \vss } } +\cs_new_protected:Npn \__atableau_valign_top:n #1 { \vbox_to_zero:n { \vss #1 } } +\cs_set_eq:NN \__atableau_valign_center:n \__atableau_valign_centre:n + +% We use \vbox_to_zero:n and \hbox_overlap_center:n to ensure that an entry +% does not change the height or width of the node when it is too large. +\cs_new_protected:Npn \__atableau_entry_math:n #1 +{ + \__atableau_valign:n + { + \__atableau_halign:n + { + \tl_if_blank:VF \tikz@textcolor {\color{\tikz@textcolor}} + \tikz@textfont $~#1 $ + } + } +} + +% and a text version +\cs_new_protected:Npn \__atableau_entry_text:n #1 +{ + \__atableau_valign:n + { + \__atableau_halign:n + { + \tl_if_blank:VF \tikz@textcolor {\color{\tikz@textcolor}} + \tikz@textfont #1 + } + } +} + +% By default tableau nodes are typeset in math-mode. +\cs_set_eq:NN \__atableau_entry:n \__atableau_entry_math:n + +% usage: \__atableau_draw_entry:nn [style] {entry} +% Used by draw_tableau to draw the node entry #2 in the tableau using the style #1 +\cs_new_protected:Npn \__atableau_draw_entry:nn [#1] #2 +{ + % exit when we reach the end of the row + \quark_if_recursion_tail_stop_do:nn {#2} + { + % record the column index in the shape for drawing the border + \seq_put_right:NV \l__atableau_shape_seq \l__atableau_col_int + } + + % compute the node name and its (x,y)-coordinates + \__atableau_set_box_coordinates:nVV {l} \l__atableau_row_int \l__atableau_col_int + + \tl_if_empty:nTF {#1} + { % draw box if it has the default styling + \node[aTableau/boxStyle] (\l__atableau_name_tl) + at (\fp_use:N\l__atableau_xl_fp, \fp_use:N\l__atableau_yl_fp) + {\__atableau_entry:n{#2}}; + } + { % save the node to \l__atableau_styled_nodes_tl if it is styled + \tl_put_right:Nx \l__atableau_styled_nodes_tl + { + \exp_not:N \node [aTableau/boxStyle,#1] (\l__atableau_name_tl) + at (\fp_use:N\l__atableau_xl_fp, \fp_use:N\l__atableau_yl_fp) + {\exp_not:N\__atableau_entry:n{#2}}; + } + } + + % look for the next entry, or finish + \int_incr:N \l__atableau_col_int + \__atableau_peek_tableau:w +} + +% --------------------------------------------------------------------------- +% diagrams + +% usage: \__atableau_draw_diagram:n {partition} +\cs_new_protected:Npn \__atableau_draw_diagram:n #1 +{ + % convert #1 to the partition \l__atableau_shape_seq + \__atableau_set_partition:nn {shape} {#1} + + % set the skew shape for shifted tableaux + \bool_if:NT \l__atableau_shifted_bool + { + \seq_clear:N \l__atableau_skew_seq + \seq_map_inline:Nn \l__atableau_shape_seq + { + \seq_put_right:No \l__atableau_skew_seq {\int_use:N \l__atableau_row_int} + \int_incr:N \l__atableau_row_int + } + \int_zero:N \l__atableau_row_int + } + + % depending on \l__atableau_show_tl, generate the tableau entries + \str_case:VnF \l__atableau_show_tl + { + {contents} { \__atableau_shape_to_content:N \l__atableau_shape_seq } + {last} { \__atableau_shape_to_last:N \l__atableau_shape_seq } + {hooks} { \__atableau_shape_to_hook:N \l__atableau_shape_seq } + {first} { \__atableau_shape_to_first:N \l__atableau_shape_seq } + {residues} { \__atableau_shape_to_residue:N \l__atableau_shape_seq } + {} { \__atableau_diagram_for_shape:N \l__atableau_shape_seq } + } + { + \msg_error:nnx {aTableau} {unrecognised-entries} {\l__atableau_show_tl} + } + \__atableau_draw_tableau:V \l__atableau_entries_tl +} + + +% --------------------------------------------------------------------------- +% multitableau and their diagrams + +% usage: \__atableau_draw_multishape:n diagram|tableau +% Draw a multitableau or multidiagram. Most of the work is in calculating the +% x-coordinates of the origins and each diagram, their separators and the +% maximal y-coordinates, for drawing the delimiters. We also need to set +% various keys for the components, so that they work correctly. +\cs_new_protected:Npn \__atableau_draw_multishape:n #1 +{ + % save the prefix name so that we can modify it + \tl_set_eq:NN \l__atableau_multiprefix_tl \l__atableau_prefix_tl + + % check for conjugation + \bool_if:NT \l__atableau_conjugate_bool { \seq_reverse:N \l__atableau_component_seq } + + % reset the variables that we need + \int_zero:N \l__atableau_component_int % component index + + % We will increment x_fp to give the origins of the component diagrams. + % For now we record the position of the x-coordinates of the left brace, + % which will be placed after the diagrams have been drawn we first have to + % determine their height. + \seq_clear:N \l__atableau_xsep_seq + \seq_put_right:Nx \l__atableau_xsep_seq {\fp_to_decimal:N\l__atableau_x_fp} + + % keep track of min/max y-coordinates used and max x-coordinate + \fp_set_eq:NN \l__atableau_xmax_fp \l__atableau_x_fp + \fp_set:Nn \l__atableau_ymax_fp {\l__atableau_y_fp + \l__atableau_tab_row_dy_fp*\l__atableau_box_ht_fp/2} % middle of box + \fp_set_eq:NN \l__atableau_ymin_fp \l__atableau_ymax_fp + + \seq_map_inline:Nn \l__atableau_component_seq + { + % increment component and set prefix, charge, skew, ribbons and snobs + \int_incr:N \l__atableau_component_int + + % update ymin, ymax and xmax (re-enable each time as this is disabled when placing ribbons) + \fp_compare:nNnTF {\l__atableau_rows_fp} = {0} + { \cs_set_eq:NN \__atableau_update_extrema:n \__atableau_update_multi_extrema:n } % update xmax, ymin, ymax + { \cs_set_eq:NN \__atableau_update_extrema:n \__atableau_update_multi_xtrema:n } % update only xmax + + % change the node prefix to include the component index + \tl_set:No \l__atableau_prefix_tl {\l__atableau_multiprefix_tl-\int_use:N\l__atableau_component_int} + + % charge defaults to zero if not set + \int_set:Nn \l__atableau_charge_int {0\seq_item:NV \l__atableau_charge_seq \l__atableau_component_int} + + % set the multi-component keys from the corresponding multi sequence + \clist_map_inline:nn {dotted_rows, dotted_cols, paths, ribbons, snobs} + { + % if multi####1 is empty then clear the ####1 sequence, otherwise set it equal to component value + \seq_if_empty:cTF {l__atableau_multi####1_seq} + { \seq_clear:c {l__atableau_####1_seq} } + { + \tl_set:Nx \l_tmpb_tl {\seq_item:cV {l__atableau_multi####1_seq} \l__atableau_component_int} + \seq_set_from_clist:co {l__atableau_####1_seq} {\l_tmpb_tl} + } + } + + % labels are handled separately because they are not sequences + \seq_if_empty:NTF \l__atableau_multilabel_seq + { \seq_clear:N \l__atableau_label_seq } + { + \tl_set:Nx \l_tmpb_tl {\seq_item:NV \l__atableau_multilabel_seq \l__atableau_component_int} + \tl_set:No \l__atableau_label_tl {\l_tmpb_tl} + } + + % skew is handled separately because it uses set_partition + \seq_if_empty:NTF \l__atableau_multiskew_seq + { \seq_clear:N \l__atableau_skew_seq } + { + \tl_set:Nx \l_tmpb_tl {\seq_item:NV \l__atableau_multiskew_seq \l__atableau_component_int} + \__atableau_set_partition:no {skew} {\l_tmpb_tl} + } + + % determine the coordinates for the diagram/tableau + % - \l__atableau_c_int: number of columns in first row + % - \l__atableau_r_int: number of rows in components + \bool_if:nTF { \str_if_empty_p:n {##1} || \str_if_eq_p:nn {##1} {...} } + { + \int_set:Nn \l__atableau_c_int {1} + \int_set:Nn \l__atableau_r_int {1} + } + { + % the component is nonempty + \tl_if_eq:nnTF {#1} {diagram} + { + \__atableau_set_partition:nn {shape} {##1} + \int_set:No \l__atableau_c_int {\seq_item:Nn \l__atableau_shape_seq {1}+0\seq_item:Nn \l__atableau_skew_seq {1}} + + % set r_int equal to the number of nonzero rows in shape_seq + \int_set:No \l__atableau_r_int { \seq_count:N \l__atableau_shape_seq } + } + { % coordinates for tableaux + \seq_set_from_clist:Nn \l_tmpa_seq {##1} + \int_set:No \l__atableau_c_int {0\seq_item:Nn \l__atableau_skew_seq {1}} % initialise to skew length + \tl_set:Nx \l_tmpc_tl {\seq_item:Nn \l_tmpa_seq {1}} % first row of tableau + \__atableau_count_row:x \l_tmpc_tl % length of first row + skew + + % set r_int equal to the number of nonzero rows in shape_seq + \int_set:No \l__atableau_r_int { \seq_count:N \l_tmpa_seq } + } + } + + % now that we have the coordinates we need, we compute the + % x-coordinates of the diagram origin and the separators + + % need to switch for conjugate partitions + \bool_if:NTF \l__atableau_conjugate_bool + { % conjugating + % the origin is c * col_dx units from the separator + xoffset + \fp_add:Nn \l__atableau_x_fp + { + abs(\l__atableau_c_int*\l__atableau_tab_row_dx_fp*\l__atableau_box_wd_fp) % number of columns + + 0\seq_item:NV \l__atableau_xoffsets_seq \l__atableau_component_int % x-offset + } + % the next separator is is r * row_dx units from the origin + \fp_set:Nn \l__atableau_xsep_fp { + abs(\l__atableau_r_int*\l__atableau_tab_col_dx_fp*\l__atableau_box_wd_fp) + + \l__atableau_separation_fp + } + + % compute maximum height of the diagram + \fp_set:Nn \l__atableau_yb_fp + { + \l__atableau_c_int*\l__atableau_tab_row_dy_fp*\l__atableau_box_ht_fp + + \l__atableau_r_int*abs(\l__atableau_tab_col_dy_fp)*\l__atableau_box_ht_fp + + 0\seq_item:NV \l__atableau_yoffsets_seq \l__atableau_component_int + } + } + { % not conjugating + % the origin is r * row_dx units from the separator + xoffset + \fp_add:Nn \l__atableau_x_fp + { + abs(\l__atableau_r_int*\l__atableau_tab_row_dx_fp*\l__atableau_box_wd_fp) % number of columns + + 0\seq_item:NV \l__atableau_xoffsets_seq \l__atableau_component_int % x-offset + } + % the next separator is is c * col_dx units from the origin + \fp_set:Nn \l__atableau_xsep_fp + { + abs(\l__atableau_c_int*\l__atableau_tab_col_dx_fp*\l__atableau_box_wd_fp) % number of columns + + \l__atableau_separation_fp + } + % compute maximum height of the diagram + \fp_set:Nn \l__atableau_yb_fp + { + \l__atableau_r_int*\l__atableau_tab_row_dy_fp*\l__atableau_box_ht_fp + + \l__atableau_c_int*\l__atableau_tab_col_dy_fp*\l__atableau_box_ht_fp + + 0\seq_item:NV \l__atableau_yoffsets_seq \l__atableau_component_int + } + } + + % Having determined the positions of the diagram origins and separators, we are ready to draw the diagram + % First set the y-coordinate for the origin of the current component + \fp_add:Nn \l__atableau_y_fp {0\seq_item:NV \l__atableau_yoffsets_seq \l__atableau_component_int} + + % special processing for empty diagrams and ... + \str_case:nnF {##1} + { + {} + { + % an empty diagram -> \l__atableau_empty_tl + \__atableau_set_box_coordinates:nnn {a} {0} {0} + \node[aTableau/separatorSymbol] at (\fp_use:N \l__atableau_xa_fp, \fp_use:N \l__atableau_ya_fp){\l__atableau_empty_tl}; + } + {...} + { + % insert dots. ?? Replace \cdots with \l__atableau_dots_tl ?? + \__atableau_set_box_coordinates:nnn {a} {0} {0} + \node[aTableau/separatorSymbol] at (\fp_use:N \l__atableau_xa_fp, \fp_use:N \l__atableau_ya_fp){$\cdots$}; + } + } + { + % draw the diagram/tableau + \use:c {__atableau_draw_#1:n} {##1} + } + + % increment the origin by the separation distance and record the + % x-coordinate of the separator + \fp_set:Nn \l__atableau_x_fp { \l__atableau_xmax_fp+\l__atableau_box_wd_fp/2+\l__atableau_separation_fp } + \seq_put_right:Nx \l__atableau_xsep_seq {\fp_to_decimal:N\l__atableau_x_fp} + + % add the separation distance to x_fp for the next component + \fp_add:NV \l__atableau_x_fp \l__atableau_separation_fp % separation + + } % end of seq_map_inline to draw component diagrams/tableau + + % All of the component diagrams/tableaux have been drawn + % It remains to add the separators. First we adjust ymin and ymax + \bool_if:NT \l__atableau_separators_bool + { + + % when rows_fp is nonzero it sets the maximum y-coordinate, otherwise we + % need to adjust ymin and ymax by half the box height + \fp_compare:nNnTF {\l__atableau_rows_fp} > {0} + { + \fp_compare:nNnTF {\l__atableau_tab_row_dy_fp} > {0} + { + \fp_add:Nn \l__atableau_ymax_fp { (\l__atableau_rows_fp-0.5)*\l__atableau_box_ht_fp } + \fp_add:Nn \l__atableau_ymin_fp { -0.5*\l__atableau_box_ht_fp } + } + { + \fp_add:Nn \l__atableau_ymin_fp { (0.5-\l__atableau_rows_fp)*\l__atableau_box_ht_fp } + \fp_add:Nn \l__atableau_ymax_fp { 0.5*\l__atableau_box_ht_fp } + } + } + { + % adjust ymin and ymax count as they count from the centre of the box + \fp_add:Nn \l__atableau_ymax_fp { \l__atableau_box_ht_fp/2} + \fp_add:Nn \l__atableau_ymin_fp {-\l__atableau_box_ht_fp/2} + } + + \fp_set:Nn \l__atableau_y_fp {(\l__atableau_ymin_fp+\l__atableau_ymax_fp)/2} % midway between ymin and ymax + \fp_set:Nn \l__atableau_ymax_fp { \l__atableau_ymax_fp-\l__atableau_ymin_fp } % maximum height + + % add left delimiter: need to use \path to set the colour + \seq_pop_left:NN \l__atableau_xsep_seq \l_tmpa_tl + \tl_if_blank:VF \l__atableau_left_delimiter_tl + { + \path[aTableau/delimiterPath] (\fp_eval:n{\l_tmpa_tl-\l__atableau_separation_fp/2}, \fp_use:N \l__atableau_y_fp) + node[aTableau/leftDelimiter] {}; + } + + % add right delimiter + \seq_pop_right:NN \l__atableau_xsep_seq \l_tmpa_tl + \tl_if_blank:VF \l__atableau_right_delimiter_tl + { + \path[aTableau/delimiterPath] (\fp_eval:n{\l_tmpa_tl-\l__atableau_separation_fp/2}, \fp_use:N \l__atableau_y_fp) + node[aTableau/rightDelimiter] {}; + } + + % the internal separators + \tl_set:Ne \l_tmpa_tl {\fp_to_decimal:N \l__atableau_ymin_fp } % ymin + \tl_set:Ne \l_tmpb_tl {\fp_to_decimal:n {\l__atableau_y_fp + \l__atableau_ymax_fp/2}} % ymax + \seq_map_inline:Nn \l__atableau_xsep_seq + { + % add the separator + \str_case:VnF \l__atableau_separator_tl + { + {|} { \draw[aTableau/separatorLine](##1,\l_tmpa_tl)--(##1,\l_tmpb_tl); } + } + { % any other separator is assumed to be text + \node[aTableau/separatorSymbol] at (##1,\fp_use:N \l__atableau_y_fp){\l__atableau_separator_tl}; + } + } + } +} + + +% usage: \__atableau_multidiagram:n {entries} +% The entries are first primarily because the \Diagram commands needs +% to force the entries to expand +\cs_new_protected:Npn \__atableau_multidiagram:n #1 +{ + % separate the component partitions + \seq_set_split:Nnn \l__atableau_component_seq {|} {#1} + + % when entries=last, we need to set the charge + \tl_if_eq:NnT \l__atableau_show_tl {last} + { + \seq_clear:N \l__atableau_charge_seq + \int_zero:N \l__atableau_c_int % cumulative total of component sizes + \seq_set_eq:NN \l_tmpc_seq \l__atableau_component_seq + \seq_reverse:N \l_tmpc_seq + \seq_map_inline:Nn \l_tmpc_seq + { + \seq_put_left:No \l__atableau_charge_seq {\int_use:N \l__atableau_c_int} + \__atableau_set_partition:nn {shape} {##1} + \seq_map_inline:Nn \l__atableau_shape_seq { \int_add:Nn \l__atableau_c_int {####1} } + } + } + + % determine the coordinates of the components of the diagram + \__atableau_draw_multishape:n {diagram} +} + +% usage: \__atableau_multitableau:n {entries} +% The entries are first primarily because the \Diagram commands need +% to force the entries to expand +\cs_new_protected:Npn \__atableau_multitableau:n #1 +{ + % separate the entries of the component tableaux + \seq_set_split:Nnn \l__atableau_component_seq {|} {#1} + + % determine the coordinates of the components of the tableau + \__atableau_draw_multishape:n {tableau} +} + +% --------------------------------------------------------------------------- +% Ribbon tableaux + +% usage: \__atableau_ribbon_tableau:n {ribbons} +% Draw a ribbon tableau. The ribbons are specified by +% (ribbon style) ij sequences of r's and c's with optional style and +% with text as a subscript. Here i and j are the row and column +% indices of the head of the ribbon +\cs_new_protected:Npn \__atableau_ribbon_tableau:n #1 +{ + % set the star style + \tl_set:Nn \l__atableau_starstyle_tl {aTableau/tableauStarStyle} + + % record the shape as we draw the border + \seq_clear:N \l__atableau_shape_seq + \cs_set_eq:NN \__atableau_update_shape: \__atableau_update_ribbon_tableau_shape: + + % draw the ribbon tableau by drawing each of the ribbons + \tl_set:Nn \l__atableau_ribbon_type_tl {ribbon} % change ribbon type to ribbon + \clist_map_inline:nn {#1} { \__atableau_add_ribbon:n {##1} } + + % draw the tableau border, adding associated bells and whistles + \__atableau_finalise_tableau: +} + +% usage: \__atableau_add_ribbon:n {ribbon} add a ribbon to the tableau +% The code for adding ribbons is slightly different dependning onf on whether +% \l__atableau_ribbbon_ty[e is equal to 'ribbon' or 'path' +\cs_new_protected:Npn \__atableau_add_ribbon:n #1 +{ + % reset the sequences that store the ribbon specifications data + \seq_clear:N \l__atableau_texts_seq % will contain node text + \seq_clear:N \l__atableau_styles_seq % will contain node styles + \seq_clear:N \l__atableau_rcs_seq % will contain node (row,col)-indices + \__atableau_peek_ribbon_style:w #1 \q_recursion_tail \q_recursion_stop +} + +% usage: \__atableau_peek_ribbon_style:w {ribbon specifications} +% look for (ribbon) style inside parentheses: (style) +\cs_new_protected:Npn \__atableau_peek_ribbon_style:w +{ + \peek_remove_spaces:n { % ignore spaces + \peek_charcode:NTF ( + { \__atableau_save_ribbon_style:n } + { \__atableau_save_ribbon_style:n ()} + } +} + +% usage: \__atableau_save_ribbon_style:n {style} +% read the style (style) and save in \l__atableau_ribbon_style_tl +% and then peek for [style]rc... +\cs_new_protected:Npn \__atableau_save_ribbon_style:n (#1) +{ + \tl_set:Nn \l__atableau_ribbon_style_tl {#1} + \__atableau_peek_style:nw {save_ribbon_head:nnn} +} + + +% usage: \__atableau_initialise_path_head: +% Start \l__atableau_ribbon_path_tl for a path +\cs_new_protected:Nn \__atableau_initialise_path_head: +{ % adding a ribbon path + \tl_set:Nx \l__atableau_ribbon_path_tl + { + (\fp_use:N\l__atableau_xl_fp,\fp_use:N\l__atableau_yl_fp) + node[aTableau/pathBox,\l__atableau_ribbon_style_tl]{\__atableau_entry:n{\l__atableau_path_box_tl}} + } +} + +% usage: \__atableau_initialise_ribbon_head: +% Start \l__atableau_ribbon_path_tl for a ribbon +\cs_new_protected:Nn \__atableau_initialise_ribbon_head: +{ + % 1. Make (xl,yl) the top corner of the ribbon and add it + \fp_add:Nn \l__atableau_xl_fp { 0.5*(\l__atableau_tab_col_dx_fp-\l__atableau_tab_row_dx_fp)*\l__atableau_box_wd_fp } + \fp_add:Nn \l__atableau_yl_fp { 0.5*(\l__atableau_tab_col_dy_fp-\l__atableau_tab_row_dy_fp)*\l__atableau_box_ht_fp } + \tl_set:Nx \l__atableau_ribbon_path_tl { (\fp_use:N\l__atableau_xl_fp,\fp_use:N\l__atableau_yl_fp) } + + % 2. Make (xa,ya) the top left of the ribbon and add it on the left (decreasing column) + \fp_set:Nn \l__atableau_xa_fp {\l__atableau_xl_fp-\l__atableau_tab_col_dx_fp*\l__atableau_box_wd_fp} + \fp_set:Nn \l__atableau_ya_fp {\l__atableau_yl_fp-\l__atableau_tab_col_dy_fp*\l__atableau_box_ht_fp} + \tl_put_left:Nx \l__atableau_ribbon_path_tl { (\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)-- } + + % 3. Make (xb,yb) the bottom right corner and add it on the right (increasing row) + \fp_set:Nn \l__atableau_xb_fp {\l__atableau_xl_fp+\l__atableau_tab_row_dx_fp*\l__atableau_box_wd_fp} + \fp_set:Nn \l__atableau_yb_fp {\l__atableau_yl_fp+\l__atableau_tab_row_dy_fp*\l__atableau_box_ht_fp} + \tl_put_right:Nx \l__atableau_ribbon_path_tl { --(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp) } +} + +% usage: \__atableau_save_ribbon_head:nnnn {path/ribbon} [style] {row index} {col index} +% save the style, row and column indices for the head of the ribbon +% and then compute the coordinates of the three "external nodes" in the +% head. +\cs_new_protected:Npn \__atableau_save_ribbon_head:nnn [#1] #2 #3 +{ + % check for syntax errors to prevent an endless loop + \quark_if_recursion_tail_stop_do:nn {#2} { \msg_error:nnn {aTableau} {invalid-ribbon-head} {no~x-coordinate~given} } + \quark_if_recursion_tail_stop_do:nn {#3} { \msg_error:nnn {aTableau} {invalid-ribbon-head} {no~y-coordinate~given} } + + % save any style + \seq_put_right:Nx \l__atableau_styles_seq {#1} + + % record the row and column indices of the head + \int_set:No \l__atableau_row_int { \int_eval:n {#2-1} } + \int_set:No \l__atableau_col_int { \int_eval:n {#3-1} } + \seq_put_right:NV \l__atableau_rcs_seq \l__atableau_row_int + \seq_put_right:NV \l__atableau_rcs_seq \l__atableau_col_int + + % update the shape to include the head node (#3,#4) + \__atableau_update_shape: + + % add initial coordinates to \l__atableau_ribbon_path_tl + \__atableau_set_box_coordinates:nVV {l} \l__atableau_row_int \l__atableau_col_int + + % initialise the start of the path/ribbon + \use:c {__atableau_initialise_ \l__atableau_ribbon_type_tl _head:} + + %\message{row=\int_use:N\l__atableau_row_int,~col=\int_use:N\l__atableau_col_int} + \__atableau_peek_ribbon_text:w +} + +% usage: \__atableau_peek_ribbon_text:nw {path/ribbon} +% peek for subscripted text _{text} in the ribbon +\cs_new_protected:Npn \__atableau_peek_ribbon_text:w +{ + \peek_remove_spaces:n { % ignore spaces + \peek_charcode_remove:NTF _ + { \__atableau_save_ribbon_text:n } + { + \seq_put_right:Nn \l__atableau_texts_seq {} % empty text + \__atableau_peek_style:nw {save_ribbon:nn} + } + } +} + +% usage: \__atableau_save_ribbon_text:n {text} +% save any text for a rode in the ribbon in \l__atableau_texts_seq +\cs_new_protected:Npn \__atableau_save_ribbon_text:n #1 +{ + \seq_put_right:No \l__atableau_texts_seq {#1} + \__atableau_peek_style:nw {save_ribbon:nn} +} + +% usage: \__atableau_add_to_ribbon:n +% Extend a ribbon path. Here #1 is either r or c +\cs_new_protected:Npn \__atableau_extend_ribbon:n #1 +{ + \str_case:enF {#1} + { + {c} { % move back one column + \int_decr:N \l__atableau_col_int + + % Move (xa,ya) back one column add it to the ribbon on the left + \fp_sub:Nn \l__atableau_xa_fp {\l__atableau_tab_col_dx_fp*\l__atableau_box_wd_fp} + \fp_sub:Nn \l__atableau_ya_fp {\l__atableau_tab_col_dy_fp*\l__atableau_box_ht_fp} + \tl_put_left:Nx \l__atableau_ribbon_path_tl { (\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)-- } + + % Move (xb,xb) back one column add it to the ribbon on the right + \fp_sub:Nn \l__atableau_xb_fp {\l__atableau_tab_col_dx_fp*\l__atableau_box_wd_fp} + \fp_sub:Nn \l__atableau_yb_fp {\l__atableau_tab_col_dy_fp*\l__atableau_box_ht_fp} + \tl_put_right:Nx \l__atableau_ribbon_path_tl { --(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp) } + } + {r} { % move forward one row + \int_incr:N \l__atableau_row_int + + % Move (xa,ya) forward one row add it to the ribbon on the left + \fp_add:Nn \l__atableau_xa_fp {\l__atableau_tab_row_dx_fp*\l__atableau_box_wd_fp} + \fp_add:Nn \l__atableau_ya_fp {\l__atableau_tab_row_dy_fp*\l__atableau_box_ht_fp} + \tl_put_left:Nx \l__atableau_ribbon_path_tl { (\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)-- } + + % Move (xb,xb) forward one row add it to the ribbon on the right + \fp_add:Nn \l__atableau_xb_fp {\l__atableau_tab_row_dx_fp*\l__atableau_box_wd_fp} + \fp_add:Nn \l__atableau_yb_fp {\l__atableau_tab_row_dy_fp*\l__atableau_box_ht_fp} + \tl_put_right:Nx \l__atableau_ribbon_path_tl { --(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp) } + } + } + { + \msg_error:nnx {aTableau} {invalid-ribbon-specification} {#1} + } + +} + +% usage: \__atableau_add_to_path:n +% Extend a ribbon path. Here #1 is either r or c +\cs_new_protected:Npn \__atableau_extend_path:n #1 +{ + + % the ribbon hasn't finished, so move row and col according to the ribbon + % specification and update the nodes (xa,ya) and (xb,yb) in the ribbon + \str_case:enF {#1} + { + {c} { % move back one column + \int_decr:N \l__atableau_col_int + + \fp_sub:Nn \l__atableau_xl_fp {\l__atableau_tab_col_dx_fp*\l__atableau_box_wd_fp} + \fp_sub:Nn \l__atableau_yl_fp {\l__atableau_tab_col_dy_fp*\l__atableau_box_ht_fp} + \tl_put_right:Nx \l__atableau_ribbon_path_tl + { + --(\fp_use:N\l__atableau_xl_fp,\fp_use:N\l__atableau_yl_fp) + node[aTableau/pathBox,\l__atableau_ribbon_style_tl]{\__atableau_entry:n{\l__atableau_path_box_tl}} + } + } + {r} { % move forward one row + \int_incr:N \l__atableau_row_int + + \fp_add:Nn \l__atableau_xl_fp {\l__atableau_tab_row_dx_fp*\l__atableau_box_wd_fp} + \fp_add:Nn \l__atableau_yl_fp {\l__atableau_tab_row_dy_fp*\l__atableau_box_ht_fp} + \tl_put_right:Nx \l__atableau_ribbon_path_tl + { + --(\fp_use:N\l__atableau_xl_fp,\fp_use:N\l__atableau_yl_fp) + node[aTableau/pathBox,\l__atableau_ribbon_style_tl]{\__atableau_entry:n{\l__atableau_path_box_tl}} + } + } + } + { + \msg_error:nnx {aTableau} {invalid-ribbon-specification} {#1} + } +} + +% For each successive r and c in the ribbon specification, determine the +% surrpouding coordinates in the ribbon and save any custom styles in +% \l__atableau_styles_seq and then repeat +\cs_new_protected:Npn \__atableau_save_ribbon:nn [#1] #2 +{ + % draw the ribbon when we run out of nodes + \quark_if_recursion_tail_stop_do:nn {#2} { \__atableau_draw_ribbon: } + + % Add the new coordinate(s) to \l__atableau_ribbon_path_tl. This is + % different for ribbons and paths + \use:c {__atableau_extend_ \l__atableau_ribbon_type_tl :n} {#2} + + % update the shape to include the new node + \__atableau_update_shape: + + % save the style and row and column indices + \seq_put_right:No \l__atableau_styles_seq {#1} % record the style of the head + \seq_put_right:NV \l__atableau_rcs_seq \l__atableau_row_int % record the row of the node + \seq_put_right:NV \l__atableau_rcs_seq \l__atableau_col_int % record the column of the node + + % check to see if this node has any text + \__atableau_peek_ribbon_text:w +} + + +% update the shape of the ribbon tableau using the current values of +% \l__atableau_row_int and \l__atableau_col_int +\cs_new_protected:Nn \__atableau_update_ribbon_tableau_shape: +{ + % ensure that \l__atableau_shape_seq has at least a 0 in each row + \int_step_inline:nn { \l__atableau_row_int+1 - \seq_count:N \l__atableau_shape_seq } + { + \seq_put_right:Nn \l__atableau_shape_seq {0} + } + % for shifted tableaux we also need to ensure that skew is big enough + \bool_if:NT \l__atableau_shifted_bool + { + \int_set:Nn \l_tmpa_int {\seq_count:N \l__atableau_skew_seq} + \int_step_inline:nn { \l__atableau_row_int+1 - \l_tmpa_int } + { + \seq_put_right:Nx \l__atableau_skew_seq {\int_eval:n{\l_tmpa_int+##1-1 }} + } + } + \int_compare:nNnT {0\seq_item:Nn \l__atableau_shape_seq {\l__atableau_row_int+1}} < {\l__atableau_col_int+1} + { + \seq_set_item:Nox \l__atableau_shape_seq {\l__atableau_row_int+1} { \int_eval:n{\l__atableau_col_int+1} } + } +} + +% usage: \__atableau_finalise_ribbon:n +% Add the final node to the ribbon +\cs_new_protected:Nn \__atableau_finalise_ribbon: +{ + % Add the last node to the ribbon + \fp_add:Nn \l__atableau_xa_fp {\l__atableau_tab_row_dx_fp*\l__atableau_box_wd_fp} + \fp_add:Nn \l__atableau_ya_fp {\l__atableau_tab_row_dy_fp*\l__atableau_box_ht_fp} + \tl_put_left:Nx \l__atableau_ribbon_path_tl { (\fp_use:N\l__atableau_xa_fp, \fp_use:N\l__atableau_ya_fp)-- } + + % add a closing cycle + \tl_put_right:Nn \l__atableau_ribbon_path_tl {--cycle} +} + + +% usage: \__atableau_finalise_path:n +% We do not need to do anything to finalise a path +\cs_set_eq:NN \__atableau_finalise_path:n \prg_do_nothing: + +% by default, snobs have the same styles and coordinates as ribbons +\cs_set_eq:NN \__atableau_extend_snob:n \__atableau_extend_ribbon:n +\cs_set_eq:NN \__atableau_finalise_snob: \__atableau_finalise_ribbon: +\cs_set_eq:NN \__atableau_initialise_snob_head: \__atableau_initialise_ribbon_head: + +% \__atableau_draw_ribbon: use the various sequences we have constructed +% to draw the ribbon. We first place the nodes with default styling and +% no text, then draw the ribbon with its supplied style and then, +% finally, add the nodes with custom styling or text. +\cs_new_protected:Nn \__atableau_draw_ribbon: +{ + + \use:c { __atableau_finalise_ \l__atableau_ribbon_type_tl :} + + % We want to first draw the ribbon, with its outline and any filling, and + % then place the node with the default styling (or unstyled), and styled + % nodes in the ribbon. To do this we build the two token lists + % \l__atableau_unstyled_nodes_tl and \l__atableau_styled_nodes_tl for these + % two types of nodes + \tl_clear:N \l__atableau_styled_nodes_tl + \tl_clear:N \l__atableau_unstyled_nodes_tl + + % We need to add ribbon_node to every box in a ribbon, so we do the + % check here rather than in \seq_map_inline:Nn + \tl_if_eq:VnTF \l__atableau_ribbon_type_tl {path} + { \tl_clear:N \l_tmpc_tl } + { \tl_set_eq:Nc \l_tmpc_tl { l__atableau_ \l__atableau_ribbon_type_tl _box_tl } } + + \seq_map_inline:Nn \l__atableau_styles_seq + { + % pop the text and row and column indices + \seq_pop_left:NN \l__atableau_texts_seq \l_tmpa_tl % text + \seq_pop_left:NN \l__atableau_rcs_seq \l_tmpb_tl % row index + \int_set:Nn \l__atableau_row_int {\l_tmpb_tl} + \seq_pop_left:NN \l__atableau_rcs_seq \l_tmpb_tl % column index + \int_set:Nn \l__atableau_col_int {\l_tmpb_tl} + + % compute the box coordinates + \__atableau_set_box_coordinates:nVV {l} \l__atableau_row_int \l__atableau_col_int + \tl_if_empty:eTF {##1\l_tmpa_tl} + { % nodes with default style and no text are added to \l__atableau_unstyled_nodes_tl + \tl_put_right:Nx \l__atableau_unstyled_nodes_tl + { + \exp_not:N \node [aTableau/\l__atableau_ribbon_type_tl Box](\l__atableau_name_tl) + at (\fp_use:N\l__atableau_xl_fp,\fp_use:N\l__atableau_yl_fp) + { \exp_not:N \__atableau_entry:n{\l_tmpc_tl }}; + } + } + { % nodes with styling are added to \l__atableau_styled_nodes_tl + \tl_put_right:Nx \l__atableau_styled_nodes_tl + { + \exp_not:N \node [aTableau/\l__atableau_ribbon_type_tl Box,##1](\l__atableau_name_tl) + at (\fp_use:N\l__atableau_xl_fp,\fp_use:N\l__atableau_yl_fp) + {\exp_not:N \__atableau_entry:n{\l_tmpa_tl}}; + } + } + } + % draw the ribbon, applying any style + \exp_last_unbraced:Ne\draw { [aTableau/\l__atableau_ribbon_type_tl Style,\l__atableau_ribbon_style_tl] } \l__atableau_ribbon_path_tl; + + % finally, add the unstyled and the styled nodes on top of the ribbon + \l__atableau_unstyled_nodes_tl + \l__atableau_styled_nodes_tl +} + + +% --------------------------------------------------------------------------- + +% abacuses +% usage: \__atableau_draw_abacus_end:nnn {abacus_top/abacus_bottom} % {0/row index} {±1} +% Draw the top/bottom on gthe abacus. Here #1 is either +% \l__atableau_abacus_top_tl or \l__atableau_abacus_bottom_tl and #2 is +% either 0, for top, or the row index of the last row, for bottom +\cs_new_protected:Npn \__atableau_draw_abacus_end:nnn #1 #2 #3 +{ + \str_case:Vn #1 + { + {-} + { % draw a line + \__atableau_set_bead_coordinates:non {a} {#2} {0} + \__atableau_set_bead_coordinates:noo {b} {#2} { \int_eval:n {\l__atableau_cols_int-1} } + \draw[aTableau/abacusEnds] + (\fp_eval:n{\l__atableau_xa_fp-\l__atableau_ab_col_dx_fp*\l__atableau_abacus_wd_fp/2}, + \fp_eval:n{\l__atableau_ya_fp-\l__atableau_ab_col_dy_fp*\l__atableau_abacus_ht_fp/2}) + -- (\fp_eval:n{\l__atableau_xb_fp+\l__atableau_ab_col_dx_fp*\l__atableau_abacus_wd_fp/2}, + \fp_eval:n{\l__atableau_yb_fp+\l__atableau_ab_col_dy_fp*\l__atableau_abacus_ht_fp/2}); + + % set default "row height" of the runner labels for use below + \fp_set:Nn \l_tmpa_fp {-0.4} + } + {_} + { % draw a line + \__atableau_set_bead_coordinates:non {a} {#2} {0} + \__atableau_set_bead_coordinates:noo {b} {#2} { \int_eval:n {\l__atableau_cols_int-1} } + \draw[aTableau/abacusEnds] (\fp_use:N\l__atableau_xa_fp, \fp_use:N\l__atableau_ya_fp) + -- (\fp_use:N\l__atableau_xb_fp, \fp_use:N\l__atableau_yb_fp); + + % set default "row height" of the runner labels for use below + \fp_set:Nn \l_tmpa_fp {-0.4} + } + {.} + { % draw dots + \int_step_inline:nn {\l__atableau_cols_int} + { + \seq_if_in:NeF \l__atableau_dotted_cols_seq { \int_eval:n{##1-1} } + { + % draw the abacus runners from (xa,ya) to (xb,yb) + \__atableau_set_bead_coordinates:noo {a} { #2 } { \int_eval:n{##1-1} } + \__atableau_set_bead_coordinates:noo {b} { #3 } { \int_eval:n{##1-1} } + \draw[aTableau/abacusEnds, dotted] (\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)--(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp); + } + } + + % set default "row height" of the runner labels for use below + \fp_set:Nn \l_tmpa_fp {-1.4} + } + {>} + { % draw dots + \int_step_inline:nn {\l__atableau_cols_int} + { + \seq_if_in:NeF \l__atableau_dotted_cols_seq { \int_eval:n{##1-1} } + { + % draw the abacus runners from (xa,ya) to (xb,yb) + \int_compare:nNnTF {#2} = {0} + { + \__atableau_set_bead_coordinates:noo {a} { \fp_eval:n{#2+0.5} } { \fp_eval:n{##1-1} } + \__atableau_set_bead_coordinates:noo {b} { \fp_eval:n{#3+0.5} } { \fp_eval:n{##1-1} } + } + { + \__atableau_set_bead_coordinates:noo {a} { \fp_eval:n{#2-0.5} } { \fp_eval:n{##1-1} } + \__atableau_set_bead_coordinates:noo {b} { \fp_eval:n{#3-0.5} } { \fp_eval:n{##1-1} } + } + \draw[aTableau/abacusEnds,->] (\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)--(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp); + } + } + + % set default "row height" of the runner labels for use below + \fp_set:Nn \l_tmpa_fp {-0.8} + } + + {*} + { % draw dots + \int_step_inline:nn {\l__atableau_cols_int} + { + \seq_if_in:NeF \l__atableau_dotted_cols_seq { \int_eval:n{##1-1} } + { + % draw the abacus runners from (xa,ya) to (xb,yb) + \int_compare:nNnTF {#2} = {0} + { + \__atableau_set_bead_coordinates:noo {a} { \fp_eval:n{#2} } { \fp_eval:n{##1-1} } + \__atableau_set_bead_coordinates:noo {b} { \fp_eval:n{#3-0.5} } { \fp_eval:n{##1-1} } + } + { + \__atableau_set_bead_coordinates:noo {a} { \fp_eval:n{#2} } { \fp_eval:n{##1-1} } + \__atableau_set_bead_coordinates:noo {b} { \fp_eval:n{#3+0.5} } { \fp_eval:n{##1-1} } + } + \draw[aTableau/abacusEnds,dotted,->] (\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)--(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp); + } + } + + % set default "row height" of the runner labels for use below + \fp_set:Nn \l_tmpa_fp {-1.8} + } + + {|} { + % set default "row height" of the runner labels for use below + \fp_set:Nn \l_tmpa_fp {-0.4} + } + } +} + + +% usage: \__atableau_abacus:nn { #runners } { bead specifications } +\cs_new_protected:Npn \__atableau_abacus:nn #1 #2 +{ + % set the star style + \tl_set:Nn \l__atableau_starstyle_tl {aTableau/abacusStarStyle} + + \int_set:Nn \l__atableau_cols_int {#1} + + + \seq_if_empty:NF \l__atableau_runner_labels_seq + { + \int_set:No \l_tmpa_int { \seq_count:N \l__atableau_runner_labels_seq } + \int_compare:nNnF {\l__atableau_cols_int-\l_tmpa_int } = {0} + { + \msg_error:nn {aTableau} {missing-runner-labels } + } + } + + % Extract the bead positions and their styles into + % \l__atableau_shape_seq, \l__atableau_styles_seq and \l__atableau_text_tl + % We allow all of the following expressions + % m, m^r, [style]m, [style]m^2, *m, *m^2, + % m_text, m_text^r, [style]m_text, [style]m_text^2, *m_text, *m_text^2, + % m_text^r, [style]m_text^2, *m_text^2 + % where m is a part of the partition \l__atableau_shape_seq and r is + % its' exponent. First, clear all of the sequences and zero the bead + % counter + \seq_clear:N \l__atableau_shape_seq % will record the partition + \seq_clear:N \l__atableau_styles_seq % will record the bead style + \seq_clear:N \l__atableau_texts_seq % will record the bead text + \int_zero:N \l__atableau_beads_int % will record the number of beads + + % determine the partition/beta numbers + \clist_map_inline:nn {#2} + { + \__atableau_peek_style:nw {record_style:nn} ##1 \q_recursion_tail \q_recursion_stop + } + + % unless they have been set, determine the number of abacus rows + \int_compare:nNnT {\l__atableau_rows_int} = {0} + { + \bool_if:NTF \l__atableau_beta_numbers_bool + { \int_set:Nn \l__atableau_rows_int { 1+\int_div_truncate:nn { 0\seq_item:Nn \l__atableau_shape_seq 1} {#1} } } + { \int_set:Nn \l__atableau_rows_int { 1+\int_div_truncate:nn {\l__atableau_beads_int-1 + 0\seq_item:Nn \l__atableau_shape_seq 1} {#1} } } + } + + % draw the abacus runners + \int_zero:N \l__atableau_col_int + \int_step_inline:nn {\l__atableau_cols_int} + { + \int_set:Nn \l_tmpa_int {##1-1} % save recalculating this many times + % skip the runners in dotted_cols_seq + \seq_if_in:NVF \l__atableau_dotted_cols_seq \l_tmpa_int + { + % draw the abacus runners from (xa,ya) to (xb,yb) + \__atableau_set_bead_coordinates:nnV {a} { 0 } \l_tmpa_int + \__atableau_set_bead_coordinates:noV {b} { \int_eval:n{\l__atableau_rows_int+1} } \l_tmpa_int + \draw[aTableau/runnerStyle] (\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)--(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp); + + % draw ticks + \int_step_inline:nn {\l__atableau_rows_int} + { + \__atableau_set_bead_coordinates:noV {l} { ####1 } \l_tmpa_int + % add a named node + \node[aTableau/namedTick] (\l__atableau_name_tl) at (\fp_use:N\l__atableau_xl_fp,\fp_use:N\l__atableau_yl_fp){}; + % add and subtract (half) tick width + \fp_set:Nn \l__atableau_xa_fp {\l__atableau_xl_fp+\l__atableau_tick_length_fp*\l__atableau_ab_col_dx_fp} + \fp_set:Nn \l__atableau_ya_fp {\l__atableau_yl_fp+\l__atableau_tick_length_fp*\l__atableau_ab_col_dy_fp} + \fp_set:Nn \l__atableau_xb_fp {\l__atableau_xl_fp-\l__atableau_tick_length_fp*\l__atableau_ab_col_dx_fp} + \fp_set:Nn \l__atableau_yb_fp {\l__atableau_yl_fp-\l__atableau_tick_length_fp*\l__atableau_ab_col_dy_fp} + + \draw[aTableau/tickStyle,name=\l__atableau_name_tl] (\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)--(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp); + } + } + } + + % set e when entries=residues + \tl_if_eq:VnT \l__atableau_show_tl {residues} + { + % if it is not set already, then set e based on the number of runners and the Cartan type + \int_compare:nNnT {\l__atableau_e_int} = {0} + { + \str_case:Vn \l__atableau_cartan_tl + { + {A } { \int_set_eq:NN \l__atableau_e_int \l__atableau_cols_int } + {C } { \int_set:Nn \l__atableau_e_int {\l__atableau_cols_int/2} } + {AA} { \int_set:Nn \l__atableau_e_int {(\l__atableau_cols_int-1)/2} } + {DD} { \int_set:Nn \l__atableau_e_int {\l__atableau_cols_int/2-1} } + } + } + } + + % draw the beads on the abacus + \int_step_inline:nn { \l__atableau_beads_int } % finally, add the beads, with labels and styles + { + + % determine the row and column indices for the bead + \bool_if:NTF \l__atableau_beta_numbers_bool + { + \int_set:No \l_tmpa_int { \seq_item:Nn \l__atableau_shape_seq {##1} } % beta number + } + { + \int_set:No \l_tmpa_int { \l__atableau_beads_int - ##1 + \seq_item:Nn \l__atableau_shape_seq {##1} } % part -> beta number + } + \int_set:Nn \l__atableau_row_int { \int_div_truncate:nn {\l_tmpa_int} {\l__atableau_cols_int} } + \int_set:Nn \l__atableau_col_int { \int_mod:nn {\l_tmpa_int} {\l__atableau_cols_int} } + + % determine the bead coordinates: push everything 0.5 of a + % unit down to allow space of the top of the abacus + \__atableau_set_bead_coordinates:noV {l} {\int_eval:n{\l__atableau_row_int+1} } \l__atableau_col_int + + \seq_if_in:NVF \l__atableau_dotted_rows_seq \l__atableau_row_int + { + \seq_if_in:NVF \l__atableau_dotted_cols_seq \l__atableau_col_int + { + \str_case:VnF \l__atableau_show_tl + { + {betas} { \tl_set:No \l_tmpa_tl { \int_use:N\l_tmpa_int } } + {residues} { + \int_set:Nn \l_tmpb_int { \l__atableau_charge_int+\l_tmpa_int-\l__atableau_beads_int } + \tl_set:No \l_tmpa_tl { \__atableau_residue:nn {\l_tmpb_int} {\l__atableau_e_int} } + } + {rows } { + \int_set:Nn \l_tmpb_int { \l_tmpa_int-\l__atableau_beads_int+##1 } + \int_compare:nNnTF {\l_tmpb_int} > {0} + { \tl_set:No \l_tmpa_tl { ##1 } } + { \tl_set:No \l_tmpa_tl { {-} } } + } + {shape} { \tl_set:No \l_tmpa_tl { \seq_item:Nn \l__atableau_shape_seq {##1} } } + {} { \tl_set:No \l_tmpa_tl { \seq_item:Nn \l__atableau_texts_seq {##1} } } + } + { + \msg_error:nnx {aTableau} {unrecognised-abacus-label} { \l__atableau_show_tl } + } + % draw the bead with style + \tl_set:No \l_tmpb_tl { \seq_item:Nn \l__atableau_styles_seq {##1} } + \exp_last_unbraced:Nx \node{[aTableau/beadStyle, \l_tmpb_tl]} + at (\fp_use:N\l__atableau_xl_fp,\fp_use:N\l__atableau_yl_fp){\__atableau_entry:x {\l_tmpa_tl}}; + } + } + } + + % draw the top ends of the abacus -- and set \l_tmpa_fp for the runner labels + \__atableau_draw_abacus_end:nnn \l__atableau_abacus_top_tl {0} {-1} + \int_zero:N \l__atableau_c_int + + % add the runner labels using the "row height" \l_tmpa_fp + \int_zero:N \l__atableau_c_int + \seq_map_inline:Nn \l__atableau_runner_labels_seq + { + \__atableau_set_bead_coordinates:nVV {a} \l_tmpa_fp \l__atableau_c_int + \seq_if_in:NVF \l__atableau_dotted_cols_seq \l__atableau_c_int + { + \node[aTableau/runnerLabelStyle] at (\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp){ \__atableau_entry:n{##1} }; + } + \int_incr:N \l__atableau_c_int + } + + % draw the bottom ends of the abacus + \__atableau_draw_abacus_end:noo \l__atableau_abacus_bottom_tl {\int_eval:n {\l__atableau_rows_int+1}} {\int_eval:n {\l__atableau_rows_int+2}} + + % remove dotted rows and columns + \seq_if_empty:NF \l__atableau_dotted_rows_seq \__atableau_remove_dotted_abacus_rows: + \seq_if_empty:NF \l__atableau_dotted_cols_seq \__atableau_remove_dotted_abacus_cols: + +} + +% usage: \__atableau_remove_dotted_abacus_cols: +% Add dots to the columns of the abacus in \l__atableau_dotted_cols_seq +\cs_new_protected:Nn \__atableau_remove_dotted_abacus_cols: +{ + % shift in row-direction + \fp_set:Nn \l_tmpa_fp {\l__atableau_ab_row_dx_fp*\l__atableau_abacus_wd_fp} + \fp_set:Nn \l_tmpb_fp {\l__atableau_ab_row_dy_fp*\l__atableau_abacus_ht_fp} + + % take a copy of \l__atableau_dotted_cols_seq so that the pop_left's below + % do not destroy it + \seq_set_eq:NN \l_tmpb_seq \l__atableau_dotted_cols_seq + \bool_do_until:nn { \seq_if_empty_p:N \l_tmpb_seq } + { + \seq_pop_left:NN \l_tmpb_seq \l_tmpa_tl + \int_set:Nn \l__atableau_col_int {\l_tmpa_tl} + \int_set:Nn \l__atableau_c_int {\l__atableau_col_int+1} + + % LaTeX3 does not provide \seq_if_in_p:NN, so ... + \bool_set_true:N \l_tmpa_bool + \bool_do_while:nn { \l_tmpa_bool } + { + \int_set:Nn \l_tmpa_int {0\seq_item:Nn \l_tmpb_seq 1} + \int_compare:nNnTF {\l__atableau_c_int} = {\l_tmpa_int} + { + \seq_pop_left:NN \l_tmpb_seq \l_tmpa_tl + \int_incr:N \l__atableau_c_int + } + { \bool_set_false:N \l_tmpa_bool } + } + + \__atableau_set_bead_coordinates:nnV {l} { 0 } \l__atableau_col_int + + % set (xa,ya) and (xb,yb) to the "left" and "right" hand coordinates that we want remove + \fp_set:Nn \l__atableau_xa_fp {\l__atableau_xl_fp-0.35*\l__atableau_ab_col_dx_fp*\l__atableau_abacus_wd_fp} + \fp_set:Nn \l__atableau_ya_fp {\l__atableau_yl_fp-0.35*\l__atableau_ab_col_dy_fp*\l__atableau_abacus_ht_fp} + \fp_set:Nn \l__atableau_xb_fp {\l__atableau_xl_fp+(\l__atableau_c_int-\l__atableau_col_int-0.65)*\l__atableau_ab_col_dx_fp*\l__atableau_abacus_wd_fp} + \fp_set:Nn \l__atableau_yb_fp {\l__atableau_yl_fp+(\l__atableau_c_int-\l__atableau_col_int-0.65)*\l__atableau_ab_col_dy_fp*\l__atableau_abacus_ht_fp} + + % blank out any line at the top of the abacus and replace it with dots + \tl_if_in:nVT {-_} \l__atableau_abacus_top_tl + { + \draw[aTableau/clearBoxes] + (\fp_eval:n{\l__atableau_xa_fp-0.1*\l_tmpa_fp}, \fp_eval:n{\l__atableau_ya_fp-0.1*\l_tmpb_fp}) + --++(\fp_eval:n{0.2*\l_tmpa_fp},\fp_eval:n{0.2*\l_tmpb_fp}) + --(\fp_eval:n{\l__atableau_xb_fp+0.1*\l_tmpa_fp}, \fp_eval:n{\l__atableau_yb_fp+0.1*\l_tmpb_fp}) + --++(\fp_eval:n{-0.2*\l_tmpa_fp},\fp_eval:n{-0.2*\l_tmpb_fp}) + --cycle + ; + % first blank out the possible header line and replace with dots + %\draw[fill=white,draw=none](\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)--(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp); + \draw[aTableau/dottedLine](\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)--(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp); + } + + % now draw the dots + \int_zero:N \l__atableau_row_int + \int_step_inline:nn {\l__atableau_rows_int} + { + \fp_add:Nn \l__atableau_xa_fp {\l_tmpa_fp} + \fp_add:Nn \l__atableau_ya_fp {\l_tmpb_fp} + \fp_add:Nn \l__atableau_xb_fp {\l_tmpa_fp} + \fp_add:Nn \l__atableau_yb_fp {\l_tmpb_fp} + \draw[aTableau/dottedLine](\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)--(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp); + } + + % blank out any line at the bottom of the abacus and replace it with dots + \tl_if_in:nVT {-_} \l__atableau_abacus_bottom_tl + { + \fp_add:Nn \l__atableau_xa_fp {\l_tmpa_fp} + \fp_add:Nn \l__atableau_ya_fp {\l_tmpb_fp} + \fp_add:Nn \l__atableau_xb_fp {\l_tmpa_fp} + \fp_add:Nn \l__atableau_yb_fp {\l_tmpb_fp} + \draw[aTableau/clearBoxes] + (\fp_eval:n{\l__atableau_xa_fp-0.1*\l_tmpa_fp}, \fp_eval:n{\l__atableau_ya_fp-0.1*\l_tmpb_fp}) + --++(\fp_eval:n{0.2*\l_tmpa_fp},\fp_eval:n{0.2*\l_tmpb_fp}) + --(\fp_eval:n{\l__atableau_xb_fp+0.1*\l_tmpa_fp}, \fp_eval:n{\l__atableau_yb_fp+0.1*\l_tmpb_fp}) + --++(\fp_eval:n{-0.2*\l_tmpa_fp},\fp_eval:n{-0.2*\l_tmpb_fp}) + --cycle + ; + % first blank out the possible header line and replace with dots + \draw[aTableau/dottedLine](\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)--(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp); + } + } +} + +% usage: \__atableau_remove_dotted_abacus_rows: +% Add dots to the rows of the abacus in \l__atableau_dotted_rows_seq +\cs_new_protected:Nn \__atableau_remove_dotted_abacus_rows: +{ + % shift in column-direction + \fp_set:Nn \l_tmpa_fp {\l__atableau_ab_col_dx_fp*\l__atableau_abacus_wd_fp} + \fp_set:Nn \l_tmpb_fp {\l__atableau_ab_col_dy_fp*\l__atableau_abacus_ht_fp} + + % take a copy of \l__atableau_dotted_rows_seq so that the pop_left's below + % do not destroy it + \seq_set_eq:NN \l_tmpb_seq \l__atableau_dotted_rows_seq + \bool_do_until:nn { \seq_if_empty_p:N \l_tmpb_seq } + { + \seq_pop_left:NN \l_tmpb_seq \l_tmpa_tl + \int_set:Nn \l__atableau_row_int {\l_tmpa_tl} + \int_set:Nn \l__atableau_r_int {\l__atableau_row_int+1} + + % LaTeX3 does not provide \seq_if_in_p:NN, so ... + \bool_set_true:N \l_tmpa_bool + \bool_do_while:nn { \l_tmpa_bool } + { + \int_set:Nn \l_tmpa_int {0\seq_item:Nn \l_tmpb_seq 1} + \int_compare:nNnTF {\l__atableau_r_int} = {\l_tmpa_int} + { + \seq_pop_left:NN \l_tmpb_seq \l_tmpa_tl + \int_incr:N \l__atableau_r_int + } + { \bool_set_false:N \l_tmpa_bool } + } + + \__atableau_set_bead_coordinates:non {l} { \int_eval:n{\l__atableau_row_int+1} } { 0 } + + % set (xa,ya) and (xb,yb) to the "left" and "right" hand coordinates that we want remove + \fp_set:Nn \l__atableau_xa_fp {\l__atableau_xl_fp-0.35*\l__atableau_ab_row_dx_fp*\l__atableau_abacus_wd_fp} + \fp_set:Nn \l__atableau_ya_fp {\l__atableau_yl_fp-0.35*\l__atableau_ab_row_dy_fp*\l__atableau_abacus_ht_fp} + \fp_set:Nn \l__atableau_xb_fp {\l__atableau_xl_fp+(\l__atableau_r_int-\l__atableau_row_int-0.65)*\l__atableau_ab_row_dx_fp*\l__atableau_abacus_wd_fp} + \fp_set:Nn \l__atableau_yb_fp {\l__atableau_yl_fp+(\l__atableau_r_int-\l__atableau_row_int-0.65)*\l__atableau_ab_row_dy_fp*\l__atableau_abacus_ht_fp} + + \draw[aTableau/clearBoxes] + (\fp_eval:n{\l__atableau_xa_fp-0.12*\l_tmpa_fp}, \fp_eval:n{\l__atableau_ya_fp-0.12*\l_tmpb_fp}) + --++(\fp_eval:n{(\l__atableau_cols_int+0.24)*\l_tmpa_fp},\fp_eval:n{(\l__atableau_cols_int+0.24)*\l_tmpb_fp}) + --(\fp_eval:n{\l__atableau_xb_fp+(\l__atableau_cols_int+0.12)*\l_tmpa_fp}, \fp_eval:n{\l__atableau_yb_fp+(\l__atableau_cols_int+0.12)*\l_tmpb_fp}) + --(\fp_eval:n{\l__atableau_xb_fp-0.12*\l_tmpa_fp}, \fp_eval:n{\l__atableau_yb_fp-0.12*\l_tmpb_fp}) + --cycle + ; + % first blank out the possible header line and replace with dots + \draw[aTableau/dottedLine](\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)--(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp); + + % now draw the dots + \int_step_inline:nnn {2} {\l__atableau_cols_int} + { + \fp_add:Nn \l__atableau_xa_fp {\l_tmpa_fp} + \fp_add:Nn \l__atableau_ya_fp {\l_tmpb_fp} + \fp_add:Nn \l__atableau_xb_fp {\l_tmpa_fp} + \fp_add:Nn \l__atableau_yb_fp {\l_tmpb_fp} + \draw[aTableau/dottedLine](\fp_use:N\l__atableau_xa_fp,\fp_use:N\l__atableau_ya_fp)--(\fp_use:N\l__atableau_xb_fp,\fp_use:N\l__atableau_yb_fp); + } + + } +} + +% --------------------------------------------------------------------------- +% Keys for the package options and their defaults + +% setting TikZ styles via styles = { ... } +\cs_new_protected:Npn \__atableau_tikzset:nn #1 #2 { \pgfqkeys{/tikz}{#1/.style={#2}} } + +% appending to aTableau styles +\cs_new_protected:Npn \__atableau_tikzset_append:nn #1 #2 { \pgfqkeys{/tikz/aTableau}{#1/.append~style={#2}} } + +% aTableau options/keys +\keys_define:nn { atableau } +{ + % --------------------------------------------------------------------------- + % general settings + + % tableaux alignment + align .choice:, + align/top .code:n = { \tikzset{baseline=(current~bounding~box.north)} }, + align/north .code:n = { \tikzset{baseline=(current~bounding~box.north)} }, + align/center .code:n = { \tikzset{baseline=(current~bounding~box.center)}}, + align/centre .code:n = { \tikzset{baseline=(current~bounding~box.center)}}, + align/bottom .code:n = { \tikzset{baseline=(current~bounding~box.south)} }, + align/south .code:n = { \tikzset{baseline=(current~bounding~box.south)} }, + align .unknown .code:n = { \msg_error:nne { aTableau } { unknown-baseline } {#1} }, + align .initial:n = centre, + + % set the Cartan type + cartan .choices:nn = { A, C, AA, DD } + { + % record the Cartan type for use in abacuses + \tl_set:Nn \l__atableau_cartan_tl {#1} + % define all of the type dependent functions here... + \cs_set_eq:Nc \__atableau_residue:nn {__atableau_residue_#1:nn} + }, + cartan/unknown .code:n = { \msg_error:nne { aTableau } { unknown-cartan } {#1} }, + cartan .initial:n = A, + + charge .code:n = + { + % To cater for multipartitions, the charge is a sequence . + % Set l__atableau_charge_int is the first item in the sequence + \regex_split:nnN {[,\|]} {#1} \l__atableau_charge_seq + \int_set:Nn \l__atableau_charge_int { \seq_item:Nn \l__atableau_charge_seq {1} } + }, + charge .value_required:n = true, + charge .initial:n = 0, + + % dotted rows and columns for tableaux and abacuses + dotted~cols .code:n = { \__atableau_set_multiseq_key:nn {dotted_cols} {#1} }, + dotted~cols .value_required:n = true, + + dotted~rows .code:n = { \__atableau_set_multiseq_key:nn {dotted_rows} {#1} }, + dotted~rows .value_required:n = true, + + e .int_set:N = \l__atableau_e_int, + e .initial:n = 0, + + entries .tl_set:N = \l__atableau_show_tl, % automatic bead labelling + entries .value_required:n = false, + entries .initial:n = , + + halign .choice:, + halign/center .code = {\cs_set_eq:NN \__atableau_halign:n \hbox_overlap_center:n}, + halign/centre .code = {\cs_set_eq:NN \__atableau_halign:n \hbox_overlap_center:n}, + halign/left .code = {\cs_set_eq:NN \__atableau_halign:n \hbox_overlap_left:n}, + halign/right .code = {\cs_set_eq:NN \__atableau_halign:n \hbox_overlap_right:n}, + halign .unknown .code:n = { \msg_error:nne { aTableau } { unknown-halign } {#1} }, + halign .initial:n = centre, + + % math/text mode for boxes and beads + math~entries .code:n = { \cs_set_eq:NN \__atableau_entry:n \__atableau_entry_math:n }, + text~entries .code:n = { \cs_set_eq:NN \__atableau_entry:n \__atableau_entry_text:n }, + + % tableau node name prefix + name .tl_set:N = \l__atableau_prefix_tl, + name .value_required:n = true, + name .initial:n = A, + + % scaling + scale .code:n = { \__atableau_set_xscale:n {#1} \__atableau_set_yscale:n {#1} }, + scale .value_required:n = true, + + star~style .code:n = { \__atableau_tikzset_append:nn {tableauStarStyle} {#1} }, + star~style .value_required:n = true, + + % shortcut for setting TikZ styles, following a suggestion of Skillmon to use \keyval_parse:nnn + styles .code:n = + { \keyval_parse:nnn { \msg_error:nnn {aTableau}{missing-style} } { \__atableau_tikzset:nn } { #1 } }, + + % tikzpicture environment + tikzpicture .tl_set:N= \l__atableau_tikzpicture_tl, + tikzpicture .value_required:n = true, + tikzpicture .initial:n =, + + % tikz code after + tikz~after .tl_set:N = \l__atableau_tikz_after_tl, + tikz~after .value_required:n = true, + tikz~after .initial:n = , + + % tikz~ code before + tikz~before .tl_set:N = \l__atableau_tikz_before_tl, + tikz~before .value_required:n = true, + tikz~before .initial:n = , + + valign .choices:nn = { bottom, center, centre, top } + { + \cs_set_eq:Nc \__atableau_valign:n { __atableau_valign_#1:n } + }, + valign .unknown .code:n = { \msg_error:nne { aTableau } { unknown-valign } {#1} }, + valign .initial:n = centre, + + xscale .code:n = { \__atableau_set_xscale:n {#1} }, + xscale .value_required:n = true, + xscale .initial:n =1, + + yscale .code:n = { \__atableau_set_yscale:n {#1} }, + yscale .value_required:n = true, + yscale .initial:n =1, + + % --------------------------------------------------------------------------- + % tableau settings + + % convention switches + Australian .code:n = \__atableau_set_style:nn {tableau} {australian}, + australian .code:n = \__atableau_set_style:nn {tableau} {australian}, + australian .value_required:n = false, + + English .code:n = \__atableau_set_style:nn {tableau} {english}, + english .code:n = \__atableau_set_style:nn {tableau} {english}, + english .value_required:n = false, + english .initial:n =, % default style + + French .code:n = \__atableau_set_style:nn {tableau} {french}, + french .code:n = \__atableau_set_style:nn {tableau} {french}, + french .value_required:n = false, + + ukrainian .code:n = \__atableau_set_style:nn {tableau} {ukrainian}, + Ukrainian .code:n = \__atableau_set_style:nn {tableau} {ukrainian}, + ukrainian .value_required:n = false, + + Russian .code:n = \__atableau_set_style:nn {tableau} {ukrainian}, + russian .code:n = \__atableau_set_style:nn {tableau} {ukrainian}, + + border .bool_set:N = \l__atableau_border_bool, + border .default:n = true, + border .initial:n = true, + + no~border .bool_set_inverse:N = \l__atableau_border_bool, + no~border .default:n = true, + + % border colours + + border~color .tl_set:N = \l__atableau_outer_tl, % an alias + border~color .value_required:n = true, + border~colour .tl_set:N = \l__atableau_outer_tl, + border~colour .value_required:n = true, + border~colour .initial:n = aTableauMain, + + border~style .code:n = { \__atableau_tikzset_append:nn {borderStyle} {#1} }, + border~style .value_required:n = true, + + % node height and width + box~height .fp_set:N = \l__atableau_box_ht_fp, + box~height .value_required:n = true, + box~height .initial:n = 0.5, + + box~width .fp_set:N = \l__atableau_box_wd_fp, + box~width .value_required:n = true, + box~width .initial:n = 0.5, + + % box styling + box~fill .tl_set:N = \l__atableau_box_fill_tl, + box~fill .value_required:n = true, + box~fill .initial:n = white, + + box~font .tl_set:N = \l__atableau_box_font_tl, + box~font .value_required:n = true, + box~font .initial:n =, + + box~shape .tl_set:N = \l__atableau_box_shape_tl, + box~shape .value_required:n = true, + box~shape .initial:n = rectangle, + + box~text .tl_set:N = \l__atableau_box_text_tl, + box~text .value_required:n = true, + box~text .initial:n = aTableauMain, + + box~style .code:n = { \__atableau_tikzset_append:nn {boxStyle} {#1} }, + box~style .value_required:n = true, + + boxes .bool_set:N = \l__atableau_boxes_bool, + boxes .default:n = true, + boxes .initial:n = true, + + no~boxes .bool_set_inverse:N = \l__atableau_boxes_bool, + no~boxes .default:n = true, + + conjugate .code:n = + { + \tl_set:Nx \l_tmpa_tl {\str_lowercase:n {#1}} + \str_if_eq:VnTF \l_tmpa_tl {true} + { + \bool_set_true:N \l__atableau_conjugate_bool + \cs_set_eq:NN \__atableau_set_box_coordinates:nnn \__atableau_set_box_coordinates_conjugate:nnn + } + { + \bool_set_false:N \l__atableau_conjugate_bool + \cs_set_eq:NN \__atableau_set_box_coordinates:nnn \__atableau_set_box_coordinates_normal:nnn + } + }, + conjugate .default:n = true, + conjugate .initial:n = false, + + inner~wall .tl_set:N = \l__atableau_inner_tl, + inner~wall .value_required:n = true, + inner~wall .initial:n = aTableauInner, + + inner~style .code:n = { \__atableau_tikzset_append:nn {innerWall} {#1} }, + inner~style .value_required:n = true, + + % label + label .code:n = { + \tl_if_in:nnTF {#1} {|} + { + % unpack the ribbons into \l__atableau_multiribbons_seq + \seq_set_split:cnn {l__atableau_multilabel_seq} {|} {#1} + } + { \tl_set:No \l__atableau_label_tl {#1} } + }, + + label~style .code:n = { \__atableau_tikzset_append:nn {labelStyle} {#1} }, + label~style .value_required:n = true, + + % -- paths --------------------------------------- + paths .code:n = { \__atableau_set_multiseq_key:nn {paths} {#1} }, + paths .initial:n = , + + path~style .code:n = { \__atableau_tikzset_append:nn {pathStyle} {#1} }, + path~style .value_required:n = true, + path~style .initial:n =, + + path~box .tl_set:N = \l__atableau_path_box_tl, + path~box .initial:n = , + + path~box~style .code:n = { \__atableau_tikzset_append:nn {pathBox} {#1} }, + path~box~style .value_required:n = true, + path~box~style .initial:n =, + + % -- ribbons --------------------------------------- + ribbons .code:n = { \__atableau_set_multiseq_key:nn {ribbons} {#1} }, + ribbons .initial:n = , + + ribbon~style .code:n = { \__atableau_tikzset_append:nn {ribbonStyle} {#1} }, + ribbon~style .value_required:n = true, + ribbon~style .initial:n =, + + ribbon~box .tl_set:N = \l__atableau_ribbon_box_tl, + ribbon~box .initial:n = , + + ribbon~box~style .code:n = { \__atableau_tikzset_append:nn {ribbonBox} {#1} }, + ribbon~box~style .value_required:n = true, + ribbon~box~style .initial:n =, + + % -- snobs --------------------------------------- + snobs .code:n = { \__atableau_set_multiseq_key:nn {snobs} {#1} }, + snobs .initial:n = , + + snob~style .code:n = { \__atableau_tikzset_append:nn {snobStyle} {#1} }, + snob~style .value_required:n = true, + snob~style .initial:n =, + + snob~box .tl_set:N = \l__atableau_snob_box_tl, + snob~box .initial:n = , + + snob~box~style .code:n = { \__atableau_tikzset_append:nn {snobBox} {#1} }, + snob~box~style .value_required:n = true, + snob~box~style .initial:n =, + + % -- shifted, skew and tabloid shapes ------------ + shifted .bool_set:N = \l__atableau_shifted_bool, + shifted .initial:n = false, + + skew .code:n = + { + \tl_if_in:nnTF {#1} {|} + { + % unpack the skew tableau into \l__atableau_multiskew_seq + \seq_set_split:Nnn \l__atableau_multiskew_seq {|} {#1} + } + { \__atableau_set_partition:nn {skew} {#1} } + }, + skew .value_required:n = true, + skew .initial:n = 0, + + skew~border .bool_set:N = \l__atableau_skew_border_bool, + skew~border .initial:n = false, + + no~skew~border .bool_set_inverse:N = \l__atableau_skew_border_bool, + no~skew~border .default:n = true, + + skew~border~style .code:n = { \__atableau_tikzset_append:nn {skewBorder} {#1} }, + skew~border~style .value_required:n = true, + + skew~boxes .bool_set:N = \l__atableau_skew_boxes_bool, + skew~boxes .default:n = true, + skew~boxes .initial:n = false, + + no~skew~boxes .bool_set_inverse:N = \l__atableau_skew_boxes_bool, + + skew~box~style .code:n = { \__atableau_tikzset_append:nn {skewBox} {#1} }, + skew~box~style .value_required:n = true, + + skew colour .tl_set:N = \l__atableau_skew_border_tl, + skew color .tl_set:N = \l__atableau_skew_border_tl, + skew colour .initial:n = aTableauSkew, + + tabloid .bool_set:N = \l__atableau_tabloid_bool, + tabloid .initial:n = false, + + % -- multitableaux and multidiagrams -------------------- + + delimiters .code:n = { \__atableau_set_delimiters:nn #1 }, + delimiters .value_required:n = true, + delimiters .initial:n = (), + + left~delimiter .tl_set:N = \l__atableau_left_delimiter_tl, + left~delimiter .value_required:n = true, + + right~delimiter .tl_set:N = \l__atableau_right_delimiter_tl, + right~delimiter .value_required:n = true, + + empty .tl_set:N = \l__atableau_empty_tl, + empty .initial:n = \textendash, + + separators .bool_set:N = \l__atableau_separators_bool, + separators .default:n = true, + separators .initial:n = true, + + no~separators .bool_set_inverse:N = \l__atableau_separators_bool, + no~separators .default:n = true, + + separation .fp_set:N = \l__atableau_separation_fp, + separation .value_required:n = true, + separation .initial:n = 0.3, + + separator .tl_set:N = \l__atableau_separator_tl, + separator .value_required:n = true, + separator .initial:n = |, + + separator~colour .tl_set:N = \l__atableau_separator_fg_tl, + separator~colour .value_required:n = true, + separator~colour .initial:n = aTableauMain, + + separator~color .tl_set:N = \l__atableau_separator_fg_tl, + separator~color .value_required:n = true, + + % set rows in abacuses, multitableau and multidiagrams + rows .code:n = { + \fp_set:Nn \l__atableau_rows_fp {#1} + \int_set:No \l__atableau_rows_int {\fp_to_int:N \l__atableau_rows_fp} + }, + rows .value_required:n = true, + rows .initial:n = 0, + + xoffsets .code:n = { \regex_split:nnN {[\|,]} {#1} \l__atableau_xoffsets_seq }, + xoffsets .value_required:n = true, + xoffsets .initial:n = 0, + + yoffsets .code:n = { \regex_split:nnN {[\|,]} {#1} \l__atableau_yoffsets_seq }, + yoffsets .value_required:n = true, + yoffsets .initial:n = 0, + + % --------------------------------------------------------------------------- + % abacus keys + + south .code:n = \__atableau_set_style:nn {abacus} {south}, + south .initial:n =, + east .code:n = \__atableau_set_style:nn {abacus} {east}, + north .code:n = \__atableau_set_style:nn {abacus} {north}, + west .code:n = \__atableau_set_style:nn {abacus} {west}, + + % abacus style + abacus~ends .code:n = { \__atableau_set_abacus_ends:nn #1 }, + abacus~ends .value_required:n = true, + abacus~ends .initial:n = {-|}, + + abacus~ends~style .code:n = { \__atableau_tikzset_append:nn {abacusEnds} {#1} }, + abacus~ends~style .value_required:n = true, + + abacus~star~style .code:n = { \__atableau_tikzset_append:nn {abacusStarStyle} {#1} }, + abacus~star~style .value_required:n = true, + + bead .tl_set:N = \l__atableau_bead_tl, % bead colour + bead .value_required:n = true, + bead .initial:n = aTableauMain, + + bead~font .tl_set:N = \l__atableau_bead_font_tl, % bead font + bead~font .initial:n = \small, + bead~font .value_required:n = true, + + bead~size .fp_set:N = \l__atableau_bead_size_fp, + bead~size .initial:n = 0.4, + + bead~sep .fp_set:N = \l__atableau_abacus_ht_fp, % bead separation + bead~sep .value_required:n = true, + bead~sep .initial:n = 0.42, + + bead~shape .tl_set:N = \l__atableau_bead_shape_tl, % bead shape colour + bead~shape .value_required:n = true, + bead~shape .initial:n = circle, + + bead~style .code:n = { \__atableau_tikzset_append:nn {beadStyle} {#1} }, + bead~style .value_required:n = true, + + bead~text .tl_set:N = \l__atableau_bead_text_tl, % bead text colour + bead~text .value_required:n = true, + bead~text .initial:n = white, + + beta~numbers .bool_set:N = \l__atableau_beta_numbers_bool, + beta~numbers .initial:n = false, + + no~shade .code:n = { \__atableau_tikzset_append:nn {beadStyle} {no~shade,} }, + no~shade .value_required:n = false, + + runner .tl_set:N = \l__atableau_runner_tl, % runner colour + runner .value_required:n = true, + runner .initial:n = aTableauInner, + + runner~style .code:n = { \__atableau_tikzset_append:nn {runnerStyle} {#1} }, + runner~style .value_required:n = true, + + runner~labels .code:n = { \seq_set_split:Nnn \l__atableau_runner_labels_seq {,} {#1} }, + runner~labels .value_required:n = true, + + runner~label~style .code:n = { \__atableau_tikzset_append:nn {runnerLabelStyle} {#1} }, + runner~label~style .value_required:n = true, + + runner~sep .fp_set:N = \l__atableau_abacus_wd_fp, % runner separation + runner~sep .value_required:n = true, + runner~sep .initial:n = 0.42, + + shading .tl_set:N = \l__atableau_shading_tl, + shading .value_required:n = true, + shading .initial:n = ball, + + tick .tl_set:N = \l__atableau_tick_tl, % tick colour + tick .initial:n = aTableauInner, + + tick~length .code:n = { \fp_set:Nn \l__atableau_tick_length_fp {#1/2} }, % (half) tick width separation + tick~length .value_required:n = true, + tick~length .initial:n = 0.1, + + tick~style .code:n = { \__atableau_tikzset_append:nn {tickStyle} {#1} }, + tick~style .value_required:n = true, +} + + +% --------------------------------------------------------------------------- +% Usage: \__atableau_set_origin:nn (x,y) +% Set the Cartesian coordinates for the corner of the (1,1) box +% TODO: allow general TikZ-coorindates. To do this we could, for example, use ideas from +% https://tex.stackexchange.com/questions/33703/extract-x-y-coordinate-of-an-arbitrary-point-in-tikz +\cs_new_protected:Npn \__atableau_set_origin:nn (#1,#2) +{ + \fp_set:Nn \l__atableau_x_fp {#1} + \fp_set:Nn \l__atableau_y_fp {#2} +} + +% usage: \__atableau_tikzpicture:nnn {settings} {origin} {aTableau command} +% where +% - #1: settings are a comma separated list of aTableau settings +% - #2: origin are Cartesian coordinates in the form x,y, or NoValue +% - #3: an internal aTableau command with parameters for drawing something +% Apply settings and wrap an aTableau command inside a tikzpicture environment. +% Add any tikzpicture environments settings and before and after TikZ commands +\cs_new_protected:Npn \__atableau_tikzpicture:nnn #1 #2 #3 +{ + \group_begin: + % keep changes to settings local by working inside a group + \keys_set:nn {atableau} {#1} + \IfNoValueTF {#2} + { % wrap inside a tikzpicture environment, placing the picture at (0,0) + \__atableau_set_origin:nn (0,0) + \exp_last_unbraced:Ne \tikz{[\l__atableau_tikzpicture_tl]} + { + \l__atableau_tikz_before_tl + #3 + \l__atableau_tikz_after_tl + } + } + { % already inside a tikzpicture environment + \__atableau_set_origin:nn (#2) + \l__atableau_tikz_before_tl + #3 + \l__atableau_tikz_after_tl + } + \group_end: +} + +% --------------------------------------------------------------------------- +% Public facing package commands + +% Almost all public-facing routines call \__atableau_tikzpicture:nnn, +% which applies the settings, sets the coordinates of the origin and +% then ensures that the requested diagram is drawn inside a tikzpicture +% environment. + +% \Abacus (x,y) [style] {#runners} {partition} +\NewDocumentCommand\Abacus{ d() O{} m m } +{ + \__atableau_tikzpicture:nnn {#2} {#1} { \__atableau_abacus:nn {#3} {#4} } +} + +% \Diagram (x,y) [style] {shape} +\NewDocumentCommand\Diagram{ d() O{} m } +{ + \__atableau_tikzpicture:nnn {#2} {#1} { \__atableau_draw_diagram:n {#3} } +} + +% \Multidiagram (x,y) [style] {entries} +\NewDocumentCommand\Multidiagram{ d() O{} m } +{ + \__atableau_tikzpicture:nnn {#2} {#1} { \__atableau_multidiagram:n {#3} } +} + +% \Multitableau (x,y) [style] {entries} +\NewDocumentCommand\Multitableau{ d() O{} m } +{ + \__atableau_tikzpicture:nnn {#2} {#1} { \__atableau_multitableau:n {#3} } +} + +% \RibbonTableau (x,y) [style] {entries} +\NewDocumentCommand\RibbonTableau{ d() O{} m } +{ + \__atableau_tikzpicture:nnn {#2} {#1} { \__atableau_ribbon_tableau:n {#3} } +} + +% \ShiftedDiagram (x,y) [style] {entries} +\NewDocumentCommand\ShiftedDiagram{ d() O{} m } +{ + \__atableau_tikzpicture:nnn {shifted,#2} {#1} { \__atableau_draw_diagram:n {#3} } +} + +% \ShiftedTableau (x,y) [style] {entries} +\NewDocumentCommand\ShiftedTableau{ d() O{} m } +{ + \__atableau_tikzpicture:nnn {shifted,#2} {#1} { \__atableau_draw_tableau:n {#3} } +} + +% \SkewDiagram (x,y) [style] {skew shape} {entries} +\NewDocumentCommand\SkewDiagram{ d() O{} m m } +{ + \__atableau_tikzpicture:nnn {skew={#3},#2} {#1} { \__atableau_draw_diagram:n {#4} } +} + +% \SkewTableau (x,y) [style] {skew shape} {entries} +\NewDocumentCommand\SkewTableau{ d() O{} m m } +{ + \__atableau_tikzpicture:nnn {skew={#3},#2} {#1} { \__atableau_draw_tableau:n {#4} } +} + +% \Tableau (x,y) [style] {entries} +\NewDocumentCommand\Tableau{ d() O{} m } +{ + \__atableau_tikzpicture:nnn {#2} {#1} { \__atableau_draw_tableau:n {#3} } +} + +% \Tabloid (x,y) [style] {entries} +\NewDocumentCommand\Tabloid{ d() O{} m } +{ + \__atableau_tikzpicture:nnn {tabloid,#2} {#1} { \__atableau_draw_tableau:n {#3} } +} + +\NewDocumentCommand\aTabset{ m }{ \keys_set:nn { atableau } {#1} } + +% --------------------------------------------------------------------------- +% Finally, now that everything is defined, process the package options. + +\IfFormatAtLeastTF { 2022-06-01 } + { \ProcessKeyOptions [ atableau ] } + { + \RequirePackage { l3keys2e } + \ProcessKeysOptions { atableau } + } + +\endinput + +% --------------------------------------------------------------------------- +% CHANGE LOG +% +% Version 1.0 +% - initial version +% - Young diagrams, tabloids, tableaux, shifted tableaux, Ukrainian tableaux, abacuses, braids +% +% Version 2.0 +% - completely rewritten using LaTeX3 +% - key interface for the tableaux options +% - macros can be used both in and outside tikzpicture environments +% - a quark-based interface allows styles to be applied to each tableau entry +% - support for different conventions (english, french, ukrainian, australian) +% - support diagrams, tableaux, including tabloids, skew and shifted tableaux and ribbon tableaux +% - stars and styles + +% --------------------------------------------------------------------------- +% +% Copyright (C) 2022-25 by Andrew Mathas <andrew.mathas@gmail.com> +% +% This work may be distributed and/or modified under the +% conditions of the LaTeX Project Public License (LPPL), either +% version 1.3c of this license or (at your option) any later +% version. The latest version of this license is in the file: +% +% http://www.latex-project.org/lppl.txt +% +% This work is "maintained" (as per LPPL maintenance status) by +% Andrew Mathas. +% +% This package consists of the files: +% atableau.ini +% atableau.pdf +% atableau.sty +% atableau.tex +% LICENSE +% README.md +% +% --------------------------------------------------------------------------- + +% end of atableau.sty |