% --------------------------------------------------------------------------- % aTableau - Andrew Mathas (C) 2022-2025 % Modified: 08:08 Friday, 10 October 2025 % --------------------------------------------------------------------------- % % A LaTeX package for symmetric group combinatorics: % - abacuses % - multitableau % - ribbon tableau % - shifted tableau % - skew tableau % - tableaux % - tabloids % - Young diagrams % % Released under the LaTeX Project Public License v1.3c or later % See http://www.latex-project.org/lppl.txt % % The package is author-maintained in the sense of this license. The % author can be contacted via email at andrew.mathas@gmail.com. % For bugs and other issues with the package please use % the package repository github.com/AndrewMathas/aTableau/ % --------------------------------------------------------------------------- % aTableau release date and version number \def\aTableau@version{2.2.0} \def\aTableau@release{2025-10-10} % --------------------------------------------------------------------------- % load TikZ early to avoid \ExplSyntaxOn...\ExplSyntaxOff wrappers \RequirePackage{tikz} \usetikzlibrary{ arrows.meta, % for arrows in abacus ends matrix, % for delimiters in multitableaux shapes.geometric, % for diamonds in Ukrainian/Australian tableaux shapes.misc, % for 'rounded rectangle' in traditional abacuses } % --------------------------------------------------------------------------- \providecommand \IfFormatAtLeastTF { \@ifl@t@r \fmtversion } % Correct for negative signs in contents being too long to fit in a tableau \ifcsname shortminus\endcsname % define the \shortminus command only if it does not already exist \else \RequirePackage{amsfonts} \DeclareMathSymbol{\shortminus}{\mathbin}{AMSa}{"39} \fi % --------------------------------------------------------------------------- \providecommand\DeclareRelease[3]{} \providecommand\DeclareCurrentRelease[2]{} \DeclareRelease{\aTableau@version}{\atableau@release}{atableau.sty} \DeclareCurrentRelease{}{\aTableau@release} \ProvidesExplPackage{atableau} {\aTableau@release} {\aTableau@version} {for symmetric group combinatorics} % --------------------------------------------------------------------------- % Give a warning if the LaTeX installation is older than TeXLive 2024 % It is not clear to me where the actual cut-off is... \NeedsTeXFormat{LaTeX2e}% [2023-01-01] \IfFormatAtLeastTF {2024-04-13} {} { \PackageWarning {aTableau} { The~aTableau~package~uses~many~features~from~LaTeX3.~Your~LaTeX~installation~is~not~very~recent.~ If~the~package~gives~you~errors,~then~you~may~need~to~update~your~LaTeX~installation~to~at~least~TeXLive~2024. } } % \__aTableau_debug:n { message } -- for debugging in development mode % \__aTableau_debug:n ** must occupy ONLY ONE LINE so that it can be removed by ctanify script** \cs_new_protected:Npn \__aTableau_debug:n #1 {\typeout{ #1 }} % --------------------------------------------------------------------------- % aTableau error messages \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]+sequences~of~r's~and~c's~with~style} \msg_new:nnnn { aTableau } { invalid-ribbon-path } { Ribbon~specifications~should~not~back-track:~rR,~Rr,~cC~and~Cc~are~not~allowed!} { The~ribbon~specifications~do~not~allow~paths~to~immediately~backtrack~by~using~rR,~Rr,~cC~or~Cc} \msg_new:nnnn { aTableau } { invalid-ribbon-specification } { Invalid~ribbon~specification~'#1':~expecting~r,~c,~R~or~C. } { Ribbon~specifications~must~be~of~the~form:~[optional~style]~sequences~of~r's,~c's,~R's~and~C's~with~style} \msg_new:nnnn { aTableau } { inside-tikzpicture } { aTableau~command~without~(x,y)-coordinates~CANNOT~be~inside~a~tikzpicture~environment! \@currenvir} { You~need~to~be~inside~a~tikzpicture~environment~whenever~you~use~an~aTableau~command~with~(x,y)-coordinates } \msg_new:nnnn { aTableau } { invalid-key } { Invalid~key~'#1'~in~aTableau~settings. } { A~list~of~supported~keys~can~be~found~in~the~aTableau~manual. } \msg_new:nnnn { aTableau } { outside-tikzpicture } { aTableau~command~with~(x,y)-coordinates~NOT~inside~a~tikzpicture~environment! } { You~need~to~be~inside~a~tikzpicture~environment~whenever~you~use~an~aTableau~command~with~(x,y)-coordinates } \msg_new:nnnn { aTableau } { many-beads } { You~have~set~'beads=\int_use:N\l__aTableau_beads_int',~yet~your~abacus~requires~'#1'~beads } { This~is~just~a~warning~message~because~your~abacus~has~more~beads~than~expected.} \msg_new:nnnn { aTableau } { many-rows } { You~have~set~'rows=\int_use:N\l__aTableau_rows_int',~yet~your~abacus~requires~'#1'~rows } { This~is~just~a~warning~message~because~your~abacus~has~more~rows~than~expected.} \msg_new:nnnn { aTableau } { quotient-components } { The~quotient~has~too~many~components:~#1 } { When~specifying~a~partition~by~its~quotient~in~an~abacus,~the~number~of~components~in~the~quotients~can~be~at~most~the~number~of~runners~in~the~abacus } \msg_new:nnnn { aTableau } { unrecognised-abacus-label } { Unrecognised~abacus~label~'#1'. } { The~possible~abacus~labels~are~beads,~residues,~rows~and~shape} \msg_new:nnnn { aTableau } { unrecognised-aTableau-command } { Unrecognised~aTableau~command~'#1' } { The~first parameter for \textbackslash NewaTableauCommand~must~be~the~name~of~one~of~the~aTableau~picture~commands } \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 } { unknown-abacus-end } { Unrecognised~abacus~ends~setting:~'#1' } { The~supported~ends~for~the~top/bottom~of~the~abacus~are:~-,~\c_underscore_str~,.~,|,~and~>} \msg_new:nnnn { aTableau } { unknown-beamer } { Unsupported~beamer~command~'#1' } { The~supported~beamer~commands~are~invisible,~only,~onslide,~uncover~and~visible,~all~of~which~should~be~entered~WITHOUT~a~backslash } \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-theme } { Unrecognised~colour~theme~option:~'#1' } { The~supported~colour~themes~are~default,~classic,~and~natural } \msg_new:nnnn { aTableau } { unknown-valign } { Unrecognised~valign~option:~#1 } { The~supported~valign~options~are~bottom,~centre~and~top } \msg_new:nnnn { aTableau } { unsupported-abacus-end } { Traditional~abacuses~are~not~compatible~with~'#1'~abacus~ends } { Traditional~abacuses~only~support~the~-,~_~and~|~abacus~end~styles~} % --------------------------------------------------------------------------- % aTableau variables % The default values are controlled by the aTableau keys \bool_new:N \l__aTableau_beta_numbers_bool% true if specifying beta numbers \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_conjugate_bool % true if drawing conjugate tableau/diagram \bool_new:N \l__aTableau_cover_border_bool% true if drawing cover border \bool_new:N \l__aTableau_cover_boxes_bool % true if drawing cover boxes \bool_new:N \l__aTableau_framed_bool % true if drawing framed abacus runners \bool_new:N \l__aTableau_separators_bool % true if drawing separators for multishapes \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_traditional_bool % true if a traditional abacus \clist_new:N \l__aTableau_adjacent_edges_clist % list of possible adjacent edges in a ribbon \fp_new:N \l__aTableau_abacus_bottom_fp % row index of the bottom of the abacus \fp_new:N \l__aTableau_abacus_col_dx_fp % change in x-coordinate between columns in an abacus \fp_new:N \l__aTableau_abacus_col_dy_fp % change in y-coordinate between columns 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_row_dx_fp % change in x-coordinate between rows in an abacus \fp_new:N \l__aTableau_abacus_row_dy_fp % change in y-coordinate between rows in an abacus \fp_new:N \l__aTableau_abacus_top_fp % row index of the top of the abacus \fp_new:N \l__aTableau_abacus_wd_fp % width of the tableaux nodes/separation between abacus runners \fp_new:N \l__aTableau_bead_height_fp % abacus bead height \fp_new:N \l__aTableau_bead_width_fp % abacus bead width \fp_new:N \l__aTableau_beam_height_fp % width of abacus rods/runners when using traditional style \fp_new:N \l__aTableau_box_col_dx_fp % change in x-coordinate between columns in a tableau \fp_new:N \l__aTableau_box_col_dy_fp % change in y-coordinate between columns in a tableau \fp_new:N \l__aTableau_box_ht_fp % height of a tableau box \fp_new:N \l__aTableau_box_row_dx_fp % change in x-coordinate between rows in a tableau \fp_new:N \l__aTableau_box_row_dy_fp % change in y-coordinate between rows in a tableau \fp_new:N \l__aTableau_box_wd_fp % width of a tableau box \fp_new:N \l__aTableau_rotate_fp % rotation angle, set by rotate key \fp_new:N \l__aTableau_rows_fp % number of rows in abacus/tableau \fp_new:N \l__aTableau_script_fp % scaling when used as a subscript \fp_new:N \l__aTableau_scriptscript_fp % scaling when used as a subsubscript \fp_new:N \l__aTableau_separation_fp % distance between multitableau \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_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_yscale_fp % scaling in the x-direction \int_new:N \l__aTableau_beads_int % number of beads on the 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 \prop_new:N \l__aTableau_colour_theme_prop% colour theme specifications \prop_new:N \l__aTableau_ribbon_prop % border paths in a ribbon (no speed difference using \prop_new_linked:N) \seq_new:N \l__aTableau_charge_seq % sequences of charges = residue/content offset \seq_new:N \l__aTableau_colours_seq % tableau fill colours \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_cover_seq % the inner partition for the cover shape \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_multicover_seq % a sequence of cover partitions 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_rcs_seq % ribbon row, column indices and dummy sequence for abacuses \seq_new:N \l__aTableau_row_labels_seq % labels for the abacus rows \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_styles_seq % ribbon/bead styles \seq_new:N \l__aTableau_texts_seq % ribbon/bead texts \seq_new:N \l__aTableau_xoffsets_seq % x-offsets for the (1,1)-nodes in a multi-tableau \seq_new:N \l__aTableau_xsep_seq % x-coordinates separators for multishapes \seq_new:N \l__aTableau_yoffsets_seq % t-offsets for the (1,1)-nodes in a multi-tableau \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_beamer_tl % name of beamer overlay command \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_cover_border_tl % cover border colour \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 % intermediate 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_paths_tl % ribbon paths to add to tableau/diagram \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_a_tl % a corner on a cell being added to a ribbon \tl_new:N \l__aTableau_ribbon_b_tl % a corner on a cell being added to a ribbon \tl_new:N \l__aTableau_ribbon_c_tl % a corner on a cell being added to a ribbon \tl_new:N \l__aTableau_ribbon_d_tl % a corner on a cell being added to a ribbon \tl_new:N \l__aTableau_ribbon_path_tl % a ribbon in the ribbon tableau \tl_new:N \l__aTableau_ribbon_style_tl % optional style used for current path< ribbon or snob \tl_new:N \l__aTableau_ribbon_type_tl % the type of ribbon, which is either path, ribbon or snob \tl_new:N \l__aTableau_ribbons_tl % ribbons to add to tableau/diagram \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_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_snob_box_tl % box entry for snobs \tl_new:N \l__aTableau_snobs_tl % snob ribbons to add to tableau/diagram \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 % --------------------------------------------------------------------------- % aTableau colour themes % We put the colour definitions into a property list so that we can easily add % them in the manual without manually copying them across \prop_set_from_keyval:Nn \l__aTableau_colour_theme_prop { % default theme default_Main = 00008B, default_Inner = 0073E6, default_Star = E6F7FF, default_Skew = 818589, default_Fill = F8F8F8, default_Rod = B3B3c2, % classic theme classic_Main = 2C2C2C, classic_Inner = 555555, classic_Star = D6D6D6, classic_Skew = 777777, classic_Fill = EAEAEA, classic_Rod = A0A0A0, % natural theme natural_Main = 5C3D2E, natural_Inner = E6C29F, natural_Star = FFF2E5, natural_Skew = A48C74, natural_Fill = FFF8F2, natural_Rod = C4A484, % AD6434 } % usage: \__aTableau_define_html_colour:nn {theme name} {html colour} % define the HTML colour aTableau#1 = #2 \cs_new_protected:Npn \__aTableau_define_html_colour:nn #1 #2 { \definecolor {aTableau#1} {HTML} {#2} } % --------------------------------------------------------------------------- % set the box fill colour when colours is used \cs_new_protected:Npn \__aTableau_set_colours:n #1 { \seq_set_split:Nnn \l__aTableau_colours_seq {,} {#1} \cs_set_eq:NN \l__aTableau_set_box_fill:nn \__aTableau_set_box_fill_colour:nn \int_set:No \l__aTableau_e_int { \seq_count:N \l__aTableau_colours_seq } \tl_if_eq:NnT \l__aTableau_cartan_tl {C} { \int_set:No \l__aTableau_e_int { \l__aTableau_e_int-1 } } } \cs_set_eq:NN \l__aTableau_set_box_fill:nn \use_none:nn % usage: \__aTableau_set_tableau_fill_colour:nn {row} {column} % Set the fill colour in a tableau for the specified row and column \cs_new_protected:Npn \__aTableau_set_box_fill_colour:nn #1 #2 { \int_set:No \l_tmpa_int { 1+\__aTableau_residue:nn { \int_eval:n{#2-#1}} {\l__aTableau_e_int} } \tl_set:Ne \l__aTableau_box_fill_tl {\seq_item:NV \l__aTableau_colours_seq \l_tmpa_int } } % --------------------------------------------------------------------------- % TikZ styling of aTableau components \tikzset{ % ----------------------------------------------------------------------- % Allow ball shading to be disabled by the 'unshaded' styl % The essential idea comes from https://tex.stackexchange.com/a/85750/234252 unshaded/.code={ \tikz@addmode{\tikz@mode@shadefalse } }, % % a shorthand, bead=, for setting the ball colour bead/.style={ball~color=#1}, % % ----------------------------------------------------------------------- % aTableau 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 border/.style = { % shifting allows us to use \__aTableau_set_box_coordinates:nnn when drawing the border shift = {(\fp_eval:n{-(\l__aTableau_box_row_dx_fp+\l__aTableau_box_col_dx_fp)/2}, \fp_eval:n{-(\l__aTableau_box_row_dy_fp+\l__aTableau_box_col_dy_fp)/2})}, line~cap = rect, very~thick, draw = \l__aTableau_outer_tl, }, % cover walls coverBorder/.style = { % shifting allows us to use \__aTableau_set_box_coordinates:nnn when drawing the border shift = {(\fp_eval:n{-(\l__aTableau_box_row_dx_fp+\l__aTableau_box_col_dx_fp)/2}, \fp_eval:n{-(\l__aTableau_box_row_dy_fp+\l__aTableau_box_col_dy_fp)/2})}, draw = \l__aTableau_cover_border_tl, thick }, % skew walls skewBorder/.style = { % shifting allows us to use \__aTableau_set_box_coordinates:nnn when drawing the border shift = {(\fp_eval:n{-(\l__aTableau_box_row_dx_fp+\l__aTableau_box_col_dx_fp)/2}, \fp_eval:n{-(\l__aTableau_box_row_dy_fp+\l__aTableau_box_col_dy_fp)/2})}, draw = \l__aTableau_skew_border_tl, thick }, % ----------------------------------------------------------------------- % styles for tableau boxes in Young diagrams aTableauBox/.style = { anchor = center, inner~sep = 0pt, rotate = \fp_use:N \l__aTableau_rotate_fp, 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, }, tableauBox/.style = { aTableau/aTableauBox, aTableau/innerWall, draw = \l__aTableau_inner_tl, fill = \l__aTableau_box_fill_tl, }, % default cover box style coverBox/.style = { aTableau/aTableauBox, aTableau/innerWall, draw = \l__aTableau_cover_border_tl, fill = aTableauFill, }, % default skew box style skewBox/.style = { aTableau/aTableauBox, aTableau/innerWall, draw = \l__aTableau_skew_border_tl, fill = aTableauFill, }, % ----------------------------------------------------------------------- % box styles for paths, ribbons and snobs pathBox/.style = { aTableau/aTableauBox, draw = none, % border disabled by default }, ribbonBox/.style = { aTableau/aTableauBox, }, snobBox/.style = { aTableau/aTableauBox, }, % ----------------------------------------------------------------------- % default path style path/.style = { draw = \l__aTableau_inner_tl, }, % default ribbon style ribbon/.style = { aTableau/innerWall, % shifting allows us to use \__aTableau_set_box_coordinates:nnn when drawing the wall shift = {(\fp_eval:n{(\l__aTableau_box_row_dx_fp+\l__aTableau_box_col_dx_fp)/2}, \fp_eval:n{(\l__aTableau_box_row_dy_fp+\l__aTableau_box_col_dy_fp)/2})}, draw = \l__aTableau_inner_tl, }, % default snob style = ribbon style snob/.style = { aTableau/innerWall, % shifting allows us to use \__aTableau_set_box_coordinates:nnn when drawing the wall shift = {(\fp_eval:n{(\l__aTableau_box_row_dx_fp+\l__aTableau_box_col_dx_fp)/2}, \fp_eval:n{(\l__aTableau_box_row_dy_fp+\l__aTableau_box_col_dy_fp)/2})}, draw = \l__aTableau_inner_tl, }, % ----------------------------------------------------------------------- % label styles label/.style = { shift = {(\fp_eval:n{-0.2*(\l__aTableau_box_row_dx_fp+\l__aTableau_box_col_dx_fp)}, \fp_eval:n{-0.2*(\l__aTableau_box_row_dy_fp+\l__aTableau_box_col_dy_fp)})}, font=\scriptsize, text = \l__aTableau_inner_tl, }, % ----------------------------------------------------------------------- % tableau star style tableauStar/.style = { fill = aTableauStar, 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... delimiter/.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/delimiter, left~delimiter = \l__aTableau_left_delimiter_tl, xshift = 2pt, }, rightDelimiter/.style = { aTableau/delimiter, right~delimiter = \l__aTableau_right_delimiter_tl, xshift = -2pt, }, % ----------------------------------------------------------------------- % abacus beads and runners % ----------------------------------------------------------------------- % style for the top and bottom of the abacus abacusEnds/.style = { aTableau/abacusRunner, >=stealth, }, % abacus star style abacusStar/.style = { text = aTableauMain, ball~color = aTableauStar, }, abacusRunner/.style = { draw = \l__aTableau_runner_tl, inner~color = \l__aTableau_runner_tl!20, outer~color = \l__aTableau_runner_tl!90, line~cap = rect, very~thick, }, abacusTick/.style = { draw = \l__aTableau_tick_tl, semithick, }, Label/.style = { anchor = center, draw = none, font = \scriptsize, inner~sep = 3pt, minimum~height = 1ex, minimum~width = 1ex, text = aTableauInner, }, rowLabel/.style = { aTableau/Label, }, runnerLabel/.style = { aTableau/Label, }, % ----------------------------------------------------------------------- % the named coordinate for an abacus tick namedTick/.style = { minimum~height = \fp_to_decimal:N \l__aTableau_bead_height_fp cm, minimum~width = \fp_to_decimal:N \l__aTableau_bead_width_fp cm, draw=none, }, % ----------------------------------------------------------------------- % styles for abacus beads abacusBead/.style = { ball~color = \l__aTableau_bead_tl, font = \l__aTableau_bead_font_tl, minimum~height = \fp_to_decimal:N \l__aTableau_bead_height_fp cm, minimum~width = \fp_to_decimal:N \l__aTableau_bead_width_fp cm, shading = \l__aTableau_shading_tl, shape = \l__aTableau_bead_shape_tl, text = \l__aTableau_bead_text_tl, anchor = center, inner~sep = 0pt, }, } % --------------------------------------------------------------------------- % set scales % 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_abacus_wd_fp { #1*\l__aTableau_abacus_wd_fp } \fp_set:Nn \l__aTableau_bead_width_fp { #1*\l__aTableau_bead_width_fp } \fp_set:Nn \l__aTableau_box_col_dx_fp { #1*\l__aTableau_box_col_dx_fp } \fp_set:Nn \l__aTableau_box_row_dx_fp { #1*\l__aTableau_box_row_dx_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 } \fp_set:Nn \l__aTableau_tick_length_fp { #1*\l__aTableau_tick_length_fp } \fp_set:Nn \l__aTableau_xscale_fp { #1 } % make scale persistent } % 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_abacus_ht_fp { #1*\l__aTableau_abacus_ht_fp } \fp_set:Nn \l__aTableau_bead_height_fp { #1*\l__aTableau_bead_height_fp } \fp_set:Nn \l__aTableau_beam_height_fp { #1*\l__aTableau_beam_height_fp } \fp_set:Nn \l__aTableau_box_col_dy_fp { #1*\l__aTableau_box_col_dy_fp } \fp_set:Nn \l__aTableau_box_ht_fp { #1*\l__aTableau_box_ht_fp } \fp_set:Nn \l__aTableau_box_row_dy_fp { #1*\l__aTableau_box_row_dy_fp } \fp_set:Nn \l__aTableau_yscale_fp { #1 } % make scale persistent } % --------------------------------------------------------------------------- % tableau styles: english, french, australian and ukrainian - called from settings % usage: \__aTableau_set_tableau_style:nnnn {col_dx}{col_dy}{row_dx}{row_dy} % Set the style/convention for tableaus \cs_new:Npn \__aTableau_set_tableau_style:nnnn #1 #2 #3 #4 { \fp_set:Nn \l__aTableau_box_col_dx_fp {#1*\l__aTableau_box_wd_fp} \fp_set:Nn \l__aTableau_box_col_dy_fp {#2*\l__aTableau_box_ht_fp} \fp_set:Nn \l__aTableau_box_row_dx_fp {#3*\l__aTableau_box_wd_fp} \fp_set:Nn \l__aTableau_box_row_dy_fp {#4*\l__aTableau_box_ht_fp} } \cs_new:Nn \__aTableau_set_tableau_style_english: { \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} \__aTableau_set_tableau_style:nnnn {1}{0}{0}{-1} } \cs_new:Nn \__aTableau_set_tableau_style_french: { \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} \__aTableau_set_tableau_style:nnnn {1}{0}{0}{1} } \cs_new:Nn \__aTableau_set_tableau_style_australian: { \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} \__aTableau_set_tableau_style:nnnn {0.5}{-0.5}{-0.5}{-0.5} } \cs_new:Nn \__aTableau_set_tableau_style_ukrainian: { \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} \__aTableau_set_tableau_style:nnnn {0.5}{0.5}{-0.5}{0.5} } % --------------------------------------------------------------------------- % abacus conventions standard and traditional - called from settings \cs_new:Nn \__aTableau_set_abacus_convention_standard: { \bool_set_false:N \l__aTableau_traditional_bool \fp_set:Nn \l__aTableau_abacus_ht_fp {\l__aTableau_yscale_fp*0.38} % ht and wd are used for the bead coordinates \fp_set:Nn \l__aTableau_abacus_wd_fp {\l__aTableau_xscale_fp*0.38} \fp_set:Nn \l__aTableau_bead_height_fp {\l__aTableau_abacus_ht_fp*0.95} % height and width give the size of the beads \fp_set:Nn \l__aTableau_bead_width_fp {\l__aTableau_abacus_wd_fp*0.95} \fp_set:Nn \l__aTableau_tick_length_fp {\l__aTableau_xscale_fp*0.1} \tl_set:Nn \l__aTableau_bead_shape_tl {circle} \tl_set:Nn \l__aTableau_tick_tl {aTableauInner} \tl_set:Nn \l__aTableau_runner_tl {aTableauInner} \cs_set_eq:NN \__aTableau_draw_abacus_end:nnnn \__aTableau_standard_end:nnnn \cs_set_eq:NN \__aTableau_draw_abacus_runner:nnn \__aTableau_standard_runner:nnn } \cs_new:Nn \__aTableau_set_abacus_convention_traditional: { \bool_set_true:N \l__aTableau_traditional_bool \fp_set:Nn \l__aTableau_abacus_ht_fp {\l__aTableau_yscale_fp*0.3} % ht and wd are used for the bead coordinates \fp_set:Nn \l__aTableau_abacus_wd_fp {\l__aTableau_xscale_fp*0.7} \fp_set:Nn \l__aTableau_bead_height_fp {\l__aTableau_abacus_ht_fp*0.95 } % height and width give the size of the beads \fp_set:Nn \l__aTableau_bead_width_fp {\l__aTableau_abacus_wd_fp*0.95 } \fp_set:Nn \l__aTableau_tick_length_fp {\l__aTableau_xscale_fp*0.10} \tl_set:Nn \l__aTableau_bead_shape_tl {rounded~rectangle} \tl_set:Nn \l__aTableau_tick_tl {aTableauRod} \tl_set:Nn \l__aTableau_runner_tl {aTableauRod} \cs_set_eq:NN \__aTableau_draw_abacus_end:nnnn \__aTableau_traditional_end:nnnn \cs_set_eq:NN \__aTableau_draw_abacus_runner:nnn \__aTableau_traditional_runner:nnn } % --------------------------------------------------------------------------- % abacus styles north, south, east and west - called from settings % usage: \__aTableau_set_abacus_style:nnnn {col_dx}{col_dy}{row_dx}{row_dy} % Set the style/convention for abacuses \cs_new:Npn \__aTableau_set_abacus_style:nnnn #1 #2 #3 #4 { \fp_set:Nn \l__aTableau_abacus_col_dx_fp {#1} \fp_set:Nn \l__aTableau_abacus_col_dy_fp {#2} \fp_set:Nn \l__aTableau_abacus_row_dx_fp {#3} \fp_set:Nn \l__aTableau_abacus_row_dy_fp {#4} } % --------------------------------------------------------------------------- % 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_multi_seq_key:nn {name} {value} % Set up a partition for keys used with multishapes \cs_new_protected:Npn \__aTableau_set_multi_shape_key:nn #1 #2 { \tl_if_in:nnTF {#2} {|} { % unpack the ribbons into \l__aTableau_multi#1_seq \seq_set_split:cnn {l__aTableau_multi#1_seq} {|} {#2} } { \__aTableau_set_partition:nn {#1} {#2} } } % usage: \__aTableau_set_multi_seq_key:nn {name} {value} % Set up a sequence for keys used with multishapes \cs_new_protected:Npn \__aTableau_set_multi_seq_key:nn #1 #2 { \tl_if_in:nnTF {#2} {|} { % unpack the ribbons into \l__aTableau_multi#1_seq \seq_set_split:cnn {l__aTableau_multi#1_seq} {|} {#2} } { \seq_set_from_clist:cn {l__aTableau_#1_seq} {#2} } } % usage: \__aTableau_set_multi_tl_key:nn {name} {value} % Set up a token list for keys used with multishapes \cs_new_protected:Npn \__aTableau_set_multi_tl_key:nn #1 #2 { \tl_if_in:nnTF {#2} {|} { % unpack the ribbons into \l__aTableau_multi#1_seq \seq_set_split:cnn {l__aTableau_multi#1_seq} {|} {#2} } { \tl_set:cn {l__aTableau_#1_tl} {#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-end} {#1}} \tl_set:Nn \l__aTableau_abacus_bottom_tl {#2} \str_if_in:nnF {-.>|\c_underscore_str*} {#2} { \msg_error:nne {aTableau} {unknown-abacus-end} {#2}} } % --------------------------------------------------------------------------- % command variants \cs_generate_variant:Nn \fp_add:Nn {NV} \cs_generate_variant:Nn \int_compare:nNnT {oNnT} \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_abacus_bead:nnn {enV} \cs_generate_variant:Nn \__aTableau_count_row:n {x} \cs_generate_variant:Nn \__aTableau_draw_tableau:n {V} \cs_generate_variant:Nn \__aTableau_define_html_colour:nn {no} \cs_generate_variant:Nn \__aTableau_entry:n {x} \cs_generate_variant:Nn \__aTableau_add_ribbon:nn {nV} \cs_generate_variant:Nn \__aTableau_put_tikz_node:nnnnn {neVnV,noVno} \cs_generate_variant:Nn \__aTableau_set_bead_coordinates:nnn {nVn, nVV, nnV, noo, non, nVo, noV, nno} \cs_generate_variant:Nn \__aTableau_set_box_coordinates:nnn {nVV, nVn, noo, noV, nnV } \cs_generate_variant:Nn \__aTableau_set_partition:nn {nV} \cs_generate_variant:Nn \__aTableau_tikz_node:nnnn {nVno} \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_nopar: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_nopar:Npn \__aTableau_residue_C:nn #1 #2 { \int_compare:nNnTF {#1} < {0} { % #1 is negative \int_compare:nNnTF {\int_mod:nn {-1*#1} {2*#2}} < {#2} { \int_eval:n { \int_mod:nn {-1*#1} {2*#2} } } { \int_eval:n { 2*#2 - \int_mod:nn {-1*#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_nopar: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 D. 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_nopar: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} } } } } % --------------------------------------------------------------------------- % 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. As a bonus, % this means that comma-separated style specifications for nodes do not need to % be enclosed in braces as the surrounding square brackets, [...], are % sufficient to capture them in \__aTableau_record_style:nn. \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} } % read custom style { \peek_charcode_remove:NTF * { \use:c{__aTableau_#1} [\l__aTableau_starstyle_tl] } % add star style { \use:c{__aTableau_#1} [] }% no style } } } % usage: \__aTableau_record_style:nn [style] {part} % 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} } % usage: \__aTableau_peek_beads:nw {type} {stream} % Look for a caret or an underscore and pass on to \__aTableau_capture_bead:nn \cs_new_protected:Npn \__aTableau_peek_beads:nw #1 { \peek_remove_spaces:n { % ignore spaces \peek_charcode_remove:NTF , { \__aTableau_record_bead: % increment bead number and capture the specifications for the next bead \int_incr:N \l__aTableau_r_int \__aTableau_peek_style:nw {record_style:nn} } { \peek_charcode_remove:NTF _ { \__aTableau_capture_bead:nn {txt} {} } { \peek_charcode_remove:NTF ^ { \__aTableau_capture_bead:nn {exp} {}} { \__aTableau_capture_bead:nn {#1} } } } } } % usage: \__aTableau_capture_bead:nn {place} {contents} % Records the bead data. Here #1 is one of part, exp or txt \cs_new_protected:Npn \__aTableau_capture_bead:nn #1 #2 { \quark_if_recursion_tail_stop_do:nn {#2} { \__aTableau_record_bead: } \tl_put_right:cn {l__aTableau_capture_#1_tl} {#2} \__aTableau_peek_beads:nw {#1} } % usage: \__aTableau_record_bead: % Records the bead data. Here #1 is one of part, exp or txt \cs_new_protected:Nn \__aTableau_record_bead: { % add the data to 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 } { \seq_put_right:NV \l__aTableau_rcs_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 } } % 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 for the key entries=... % usage: \__aTableau_compute_conjugate_partition:N {seq} % compute the conjugate partition of 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|cover} {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: ?? Remove the hack below using \seq_set_split:Nnn and 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 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 {,} } } % usage: \__aTableau_shape_to_last:N {partition sequence} % Diagrams with entries=last are drawn using the \Tableau command \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:NV \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 {,} } } % usage: \__aTableau_shape_to_first:N {partition sequence} % Diagrams with entries=first are drawn using the \Tableau command \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 hook length tableau. Some extra care needed for % shifted tableau, which use a different definition of hook length \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 {,} } } } % usage: \__aTableau_shape_to_residue:N {partition sequence} % Residue diagrams are drawn using the \Tableau command \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 {name of sequence} {name of border style} % The name of the sequence is cover, 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 for skew and cover shapes \str_case:nn {#1} { {cover} { % left column depends on l__aTableau_shape_seq \int_set:Nn \l__aTableau_c_int { 0\seq_item:Nn \l__aTableau_shape_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:NV \l__aTableau_shape_seq \l__aTableau_r_int} } } } {shape} { % left column depends on l__aTableau_skew_seq \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:NV \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_box_col_dx_fp} \fp_set:Nn \l__aTableau_ya_fp {\l__aTableau_box_col_dy_fp} % shift in row-direction \fp_set:Nn \l__aTableau_xb_fp {\l__aTableau_box_row_dx_fp} \fp_set:Nn \l__aTableau_yb_fp {\l__aTableau_box_row_dy_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:NV \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:NV \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_box_col_dx_fp} \fp_set:Nn \l__aTableau_ya_fp {\l__aTableau_box_col_dy_fp} % shift in row-direction \fp_set:Nn \l__aTableau_xb_fp {\l__aTableau_box_row_dx_fp} \fp_set:Nn \l__aTableau_yb_fp {\l__aTableau_box_row_dy_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:NV \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:NV \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_tikz_node:nnnn {style} {label} {fp-coordinate name} {entry} % Draw a TikZ node. This simplifies the code and controls expansion % TODO: not currently used! \cs_new_protected:Nn \__aTableau_tikz_node:nnnn { \node[#1] (#2) at (\fp_use:c {l__aTableau_x#3_fp}, \fp_use:c {l__aTableau_y#3_fp}) { \__aTableau_entry:n {#4} }; } % usage: \__aTableau_put_tikz_node:nnnnn {token list} {style} {label} {fp-coordinate name} {entry} % Like \__aTableau_put_tikz_node:nVno except that we append the node to the % token list given by #1 \cs_new_protected:Nn \__aTableau_put_tikz_node:nnnnn { \tl_put_right:cn {l__aTableau_#1_tl} {\node[#2] (#3) at} \tl_put_right:ce {l__aTableau_#1_tl} {(\fp_use:c {l__aTableau_x#4_fp}, \fp_use:c {l__aTableau_y#4_fp})} \tl_put_right:cn {l__aTableau_#1_tl} { {\__aTableau_entry:n {#5}}; } } % 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_box_col_dx_fp} \fp_add:Nn \l__aTableau_ya_fp {\l_tmpa_tl*\l__aTableau_box_col_dy_fp} } % add the label \node[aTableau/label] 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_cover_boxes: % Draw the cover boxes using ribbons \cs_new_protected:Nn \__aTableau_draw_cover_boxes: { \group_begin: % override the ribbon style inside this group \tikzset{aTableau/ribbonBox/.style=aTableau/coverBox} \tikzset{aTableau/ribbon/.style={draw=none,fill=none}} \int_zero:N \l__aTableau_r_int \seq_map_inline:Nn \l__aTableau_cover_seq { \int_incr:N \l__aTableau_r_int \int_set:No \l__aTableau_row_int { 0\seq_item:NV \l__aTableau_shape_seq \l__aTableau_r_int } \int_compare:nNnT {##1} > {\l__aTableau_row_int} { \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-\l__aTableau_row_int-1} {c} } \__aTableau_add_ribbon:nV {ribbon} \l_tmpa_tl } } \group_end: } % usage: \__aTableau_draw_skew_boxes: % Draw the skew boxes using ribbons \cs_new_protected:Nn \__aTableau_draw_skew_boxes: { \group_begin: % override the ribbon style inside this group \tikzset{aTableau/ribbonBox/.style=aTableau/skewBox} \tikzset{aTableau/ribbon/.style={draw=none,fill=none}} \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:nV {ribbon} \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_box_col_dx_fp} \fp_add:Nn \l__aTableau_ya_fp {\l__aTableau_box_col_dy_fp} % adding to the right-hand side \fp_add:Nn \l__aTableau_xb_fp {\l__aTableau_box_col_dx_fp} \fp_add:Nn \l__aTableau_yb_fp {\l__aTableau_box_col_dy_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_box_row_dx_fp} \fp_add:Nn \l__aTableau_ya_fp {\l__aTableau_box_row_dy_fp} % adding to the right-hand side \fp_add:Nn \l__aTableau_xb_fp {\l__aTableau_box_row_dx_fp} \fp_add:Nn \l__aTableau_yb_fp {\l__aTableau_box_row_dy_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/tableauStar} % disable interior boxes if boxes_bool is false \bool_if:NF \l__aTableau_boxes_bool { \__aTableau_tikzset_append:nn {tableauBox} {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 there was only one entry in a row. 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 \tl_if_empty:VF \l__aTableau_paths_tl { \__aTableau_add_ribbon:nV {path} \l__aTableau_paths_tl } % add ribbons \tl_if_empty:VF \l__aTableau_ribbons_tl { \__aTableau_add_ribbon:nV {ribbon} \l__aTableau_ribbons_tl } % 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: } \seq_if_empty:NF \l__aTableau_cover_seq { \bool_if:NT \l__aTableau_cover_boxes_bool { \__aTableau_draw_cover_boxes: } \bool_if:NT \l__aTableau_cover_border_bool { \__aTableau_draw_border:nn {cover} {coverBorder} } } \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} {border} } % 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_if_empty:VF \l__aTableau_snobs_tl { \__aTableau_add_ribbon:nV {snob} \l__aTableau_snobs_tl } } % --------------------------------------------------------------------------- % 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_xmax: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 : 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_box_row_dx_fp+(#3+0.5)*\l__aTableau_box_col_dx_fp } \fp_set:cn {l__aTableau_y#1_fp} {\l__aTableau_y_fp+(#2+0.5)*\l__aTableau_box_row_dy_fp+(#3+0.5)*\l__aTableau_box_col_dy_fp } \__aTableau_update_extrema:n #1 % \__aTableau_debug:n {~-~box~coordinates:~\l__aTableau_name_tl~=~(\fp_use:c{l__aTableau_x#1_fp},~\fp_use:c{l__aTableau_y#1_fp})} } % usage: \__aTableau_set_box_coordinates_conjugate:nnn : 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_box_row_dx_fp+(#2+0.5)*\l__aTableau_box_col_dx_fp } \fp_set:cn {l__aTableau_y#1_fp} {\l__aTableau_y_fp+(#3+0.5)*\l__aTableau_box_row_dy_fp+(#2+0.5)*\l__aTableau_box_col_dy_fp } \__aTableau_update_extrema:n #1 % \__aTableau_debug:n {~-~conjugate~box~coordinates:~\l__aTableau_name_tl~=~(\fp_use:c{l__aTableau_x#1_fp},~\fp_use:c{l__aTableau_y#1_fp})} } % 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 : 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. We shift all of the % row coordinates up by l__aTableau_abacus_top_fp to ensure the line at the top % of the abacus is in the expected place. \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}-\fp_to_int:n{#3}} \fp_set:cn {l__aTableau_x#1_fp} {\l__aTableau_x_fp+((#2+\l__aTableau_abacus_top_fp)*\l__aTableau_abacus_row_dx_fp+(#3)*\l__aTableau_abacus_col_dx_fp)*\l__aTableau_abacus_wd_fp } \fp_set:cn {l__aTableau_y#1_fp} {\l__aTableau_y_fp+((#2+\l__aTableau_abacus_top_fp)*\l__aTableau_abacus_row_dy_fp+(#3)*\l__aTableau_abacus_col_dy_fp)*\l__aTableau_abacus_ht_fp } % \__aTableau_debug:n {~-~bead~coordinates:~\l__aTableau_name_tl~=~(\fp_use:c{l__aTableau_x#1_fp},~\fp_use:c{l__aTableau_y#1_fp})} } % --------------------------------------------------------------------------- % tableaux boxes/nodes % By default, tableau boxes are constructed using \__aTableau_box:n = \vbox_to_zero:n % except when the rotate key is in play when we use \__aTableau_rotated_box:n % to rotate the contents clockwise through \l__aTableau_rotate_fp degrees so % that the tableau entries stay vertical \cs_set_eq:NN \__aTableau_box:n \vbox_to_zero:n \cs_new_protected:Npn \__aTableau_rotated_box:n #1 { \vbox_set_to_ht:Nnn \l_tmpa_box {0pt} {#1} \box_rotate:Nn \l_tmpa_box {-\l__aTableau_rotate_fp} \box_use_drop:N \l_tmpa_box } \cs_new_protected:Npn \__aTableau_valign_bottom:n #1 { \__aTableau_box:n { #1 \vss } } \cs_new_protected:Npn \__aTableau_valign_centre:n #1 { \__aTableau_box:n { \vss #1 \vss } } \cs_new_protected:Npn \__aTableau_valign_top:n #1 { \__aTableau_box: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 % when colours is used, set the box fill colour \l__aTableau_set_box_fill:nn \l__aTableau_row_int \l__aTableau_col_int \tl_if_empty:nTF {#1} { % draw box if it has the default styling \node[aTableau/tableauBox] (\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 \__aTableau_put_tikz_node:noVno {styled_nodes} {aTableau/tableauBox,#1} \l__aTableau_name_tl {l} {#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_box_row_dy_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_xmax: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 sequence keys from the corresponding multi sequence \clist_map_inline:nn {dotted_rows, dotted_cols} { % 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} } } % set the multi-component tl-keys from the corresponding multi sequence \clist_map_inline:nn {label, 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} \tl_set:cV {l__aTableau_####1_tl} \l_tmpb_tl } } % cover and skew are handled separately because they use set_partition \clist_map_inline:nn {cover, skew} { \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} \__aTableau_set_partition:nV {####1} \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_box_row_dx_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_box_col_dx_fp) + \l__aTableau_separation_fp } % compute maximum height of the diagram \fp_set:Nn \l__aTableau_yb_fp { \l__aTableau_c_int*\l__aTableau_box_row_dy_fp + \l__aTableau_r_int*abs(\l__aTableau_box_col_dy_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_box_row_dx_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_box_col_dx_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_box_row_dy_fp + \l__aTableau_c_int*\l__aTableau_box_col_dy_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){ \__aTableau_entry:n {\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){ \__aTableau_entry:n{\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_box_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/tableauStar} \seq_if_empty:NTF \l__aTableau_shape_seq { % record the shape as we draw the border \cs_set_eq:NN \__aTableau_update_shape: \__aTableau_update_ribbon_tableau_shape: } { % shape already set \cs_set_eq:NN \__aTableau_update_shape: \prg_do_nothing: } % draw the ribbon tableau by drawing each of the ribbons \tl_set:Nn \l__aTableau_ribbon_type_tl {ribbon} % change ribbon type to ribbon \__aTableau_peek_ribbon:w #1 \q_recursion_tail \q_recursion_stop % 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 depending on on whether % \l__aTableau_ribbbon_sty[e is equal to 'ribbon' or 'path' \cs_new_protected:Npn \__aTableau_add_ribbon:nn #1 #2 { \tl_set:Nn \l__aTableau_ribbon_type_tl {#1} % change ribbon type to ribbon \__aTableau_peek_ribbon:w #2 \q_recursion_tail \q_recursion_stop } % We peek for the commas in the ribbon specifications so that it is not % necessary to enclose "complicated" styles inside both square brackets and % braces using [{...}]. \cs_new_protected:Npn \__aTableau_peek_ribbon:w { \peek_remove_spaces:n { % ignore spaces \peek_charcode_remove:NTF , { % look for the next ribbon \__aTableau_peek_ribbon:w } { % reset the sequences that store the ribbon specifications \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 } } } % 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_set_ribbon_coordinates:nnnn #1 #2 #3 #4 % Define the four ribbon coordinates: % - \l__aTableau_ribbon_a_tl = (r,c) c----b % - \l__aTableau_ribbon_b_tl = (r-1,c) | | % - \l__aTableau_ribbon_c_tl = (r-1,c-1) | | % - \l__aTableau_ribbon_d_tl = (r,c-1) d----a \cs_new_protected:Nn \__aTableau_set_ribbon_coordinates: { \tl_set:Ne \l__aTableau_ribbon_a_tl {\int_use:N\l__aTableau_row_int|\int_use:N\l__aTableau_col_int} \tl_set:Ne \l__aTableau_ribbon_b_tl {\int_eval:n{\l__aTableau_row_int-1}|\int_use:N\l__aTableau_col_int} \tl_set:Ne \l__aTableau_ribbon_c_tl {\int_eval:n{\l__aTableau_row_int-1}|\int_eval:n{\l__aTableau_col_int-1}} \tl_set:Ne \l__aTableau_ribbon_d_tl {\int_use:N\l__aTableau_row_int|\int_eval:n{\l__aTableau_col_int-1}} } % usage: \__aTableau_add_edges_to_ribbon: % The edges on the boundary of the ribbon are stored in \l__aTableau_ribbon_prop, % where the edge is the key and the values is empty. We use a property list % because this is the only data structure that allows us to easily add and % remove entries. The edge joining (r1,c1) and (r1,c2) is stored as % r1|c1|r2|c2 if (r1,c1) > (r2,c2) % r2|c2|r1|c1 if (r2,c2) > (r1,c1) % As the vertices are ordered, we do not need to check to see if there is an % edge from X to Y, or an edge from Y to X. \cs_new_protected:Nn \__aTableau_add_edges_to_ribbon: { % add edge a->b \tl_set:Ne \l_tmpa_tl {\l__aTableau_ribbon_a_tl|\l__aTableau_ribbon_b_tl} \prop_get:NVNTF \l__aTableau_ribbon_prop \l_tmpa_tl \l_tmpv_tl { \prop_remove:NV \l__aTableau_ribbon_prop \l_tmpa_tl } { \prop_put:NVn \l__aTableau_ribbon_prop \l_tmpa_tl {} } % add edge b->c \tl_set:Ne \l_tmpa_tl {\l__aTableau_ribbon_b_tl|\l__aTableau_ribbon_c_tl} \prop_get:NVNTF \l__aTableau_ribbon_prop \l_tmpa_tl \l_tmpv_tl { \prop_remove:NV \l__aTableau_ribbon_prop \l_tmpa_tl } { \prop_put:NVn \l__aTableau_ribbon_prop \l_tmpa_tl {} } % add edge d->c \tl_set:Ne \l_tmpa_tl {\l__aTableau_ribbon_d_tl|\l__aTableau_ribbon_c_tl} \prop_get:NVNTF \l__aTableau_ribbon_prop \l_tmpa_tl \l_tmpv_tl { \prop_remove:NV \l__aTableau_ribbon_prop \l_tmpa_tl } { \prop_put:NVn \l__aTableau_ribbon_prop \l_tmpa_tl {} } % add edge a->d \tl_set:Ne \l_tmpa_tl {\l__aTableau_ribbon_a_tl|\l__aTableau_ribbon_d_tl} \prop_get:NVNTF \l__aTableau_ribbon_prop \l_tmpa_tl \l_tmpv_tl { \prop_remove:NV \l__aTableau_ribbon_prop \l_tmpa_tl } { \prop_put:NVn \l__aTableau_ribbon_prop \l_tmpa_tl {} } } % usage: \__aTableau_initialise_ribbon_head: % Initialise a ribbon by adding the first four edges to the % \l__aTableau_ribbon_prop \cs_new_protected:Nn \__aTableau_initialise_ribbon_head: { \prop_clear:N \l__aTableau_ribbon_prop \__aTableau_set_ribbon_coordinates: \__aTableau_add_edges_to_ribbon: } % usage: \__aTableau_save_ribbon_head:nnn [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~#1,#2,#3.} } \quark_if_recursion_tail_stop_do:nn {#3} { \msg_error:nnn {aTableau} {invalid-ribbon-head} {no~y-coordinate~given~#1,#2,#3.} } % 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: % make (xl,yl) point to the initial box coordinates (is this necessary??) \__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:} \__aTableau_peek_ribbon_comma:nnw {peek_ribbon_text:w} {} } % usage: \__aTableau_peek_ribbon_text:w % 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 } % set the text of this box { % set the text of this box to empty \seq_put_right:Nn \l__aTableau_texts_seq {} % look for next entry in the ribbon \__aTableau_peek_ribbon_comma:nnw {peek_style:nw} {save_ribbon:nn} } } } % usage: \__aTableau_peek_ribbon_comma:nnw {next function} {parameters} \cs_new_protected:Npn \__aTableau_peek_ribbon_comma:nnw #1 #2 { \peek_remove_spaces:n { % ignore spaces \peek_charcode_remove:NTF , { % if there is no _ indicating following text, then we might see a % comma in which case we need to start a new ribbon \__aTableau_draw_ribbon: \__aTableau_peek_ribbon:w } { \tl_if_empty:nTF {#2} { \use:c {__aTableau_#1} } { \use:c {__aTableau_#1} {#2} } } } } % 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_ribbon_comma:nnw {peek_style:nw} {save_ribbon:nn} } % usage: \__aTableau_extend_ribbon_c: % adjust the edges in \l__aTableau_ribbon_prop by pushing through the c-edge \cs_new_protected:Nn \__aTableau_extend_ribbon_c: { \int_decr:N \l__aTableau_col_int % c -> c-1 \__aTableau_set_ribbon_coordinates: \prop_get:NeNT \l__aTableau_ribbon_prop {\l__aTableau_ribbon_a_tl|\l__aTableau_ribbon_b_tl} \l_tmpc_tl { \__aTableau_add_edges_to_ribbon: } } % usage: \__aTableau_extend_ribbon_C: % adjust the edges in \l__aTableau_ribbon_prop by pushing through the C-edge \cs_new_protected:Nn \__aTableau_extend_ribbon_C: { \int_incr:N \l__aTableau_col_int % c -> c+1 \__aTableau_set_ribbon_coordinates: \prop_get:NeNT \l__aTableau_ribbon_prop {\l__aTableau_ribbon_d_tl|\l__aTableau_ribbon_c_tl} \l_tmpc_tl { \__aTableau_add_edges_to_ribbon: } } % usage: \__aTableau_extend_ribbon_R: % adjust the edges in \l__aTableau_ribbon_prop by pushing through the R-edge \cs_new_protected:Nn \__aTableau_extend_ribbon_R: { \int_decr:N \l__aTableau_row_int % r -> r-1 \__aTableau_set_ribbon_coordinates: \prop_get:NeNT \l__aTableau_ribbon_prop {\l__aTableau_ribbon_a_tl|\l__aTableau_ribbon_d_tl} \l_tmpc_tl { \__aTableau_add_edges_to_ribbon: } } % usage: \__aTableau_extend_ribbon_r: % adjust the edges in \l__aTableau_ribbon_prop by pushing through the r-edge \cs_new_protected:Nn \__aTableau_extend_ribbon_r: { \int_incr:N \l__aTableau_row_int % r -> r+1 \__aTableau_set_ribbon_coordinates: \prop_get:NeNT \l__aTableau_ribbon_prop {\l__aTableau_ribbon_b_tl|\l__aTableau_ribbon_c_tl} \l_tmpc_tl { \__aTableau_add_edges_to_ribbon: } } % usage: \__aTableau_extend_ribbon:n {direction=} % Extend a ribbon path. Here #1 is either c, C, r or R. We extend the edges of % the ribbon in \l__aTableau_ribbon_prop using the four commands above. \cs_new_protected:Npn \__aTableau_extend_ribbon:n #1 { \cs_if_exist_use:cF { __aTableau_extend_ribbon_ #1: } { % error for invalid ribbon specification \msg_error:nnn {aTableau} { invalid-ribbon-specification } {#1 } } } % usage: \__aTableau_extend_path_c: \cs_new_protected:Nn \__aTableau_extend_path_c: { \int_decr:N \l__aTableau_col_int \fp_sub:Nn \l__aTableau_xl_fp {\l__aTableau_box_col_dx_fp} \fp_sub:Nn \l__aTableau_yl_fp {\l__aTableau_box_col_dy_fp} } % usage: \__aTableau_extend_path_C: \cs_new_protected:Nn \__aTableau_extend_path_C: { \int_incr:N \l__aTableau_col_int \fp_add:Nn \l__aTableau_xl_fp {\l__aTableau_box_col_dx_fp} \fp_add:Nn \l__aTableau_yl_fp {\l__aTableau_box_col_dy_fp} } % usage: \__aTableau_extend_path_r: \cs_new_protected:Nn \__aTableau_extend_path_r: { \int_incr:N \l__aTableau_row_int \fp_add:Nn \l__aTableau_xl_fp {\l__aTableau_box_row_dx_fp} \fp_add:Nn \l__aTableau_yl_fp {\l__aTableau_box_row_dy_fp} } % usage: \__aTableau_extend_path_R: \cs_new_protected:Nn \__aTableau_extend_path_R: { \int_decr:N \l__aTableau_row_int \fp_sub:Nn \l__aTableau_xl_fp {\l__aTableau_box_row_dx_fp} \fp_sub:Nn \l__aTableau_yl_fp {\l__aTableau_box_row_dy_fp} } % 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 { \cs_if_exist_use:cTF { __aTableau_extend_path_ #1: } { \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 % surrounding 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 % TODO: rather than saving the data here, we should draw the ribbons % now -- or, rather, save this data as TikZ commands in % \l__aTableau_styled_nodes_tl and l__aTableau_unstyled_nodes_tl so % that the data can be printed as soon as the ribbon is read. This % should be faster because it will remove the need to store the % ribbon data in the \l__aTableau_capture_*_seq sequences and then % retrieve this information later in \__aTableau_draw_ribbon: % check to see if this node has any text \__aTableau_peek_ribbon_comma:nnw {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:nnn {0} { \l__aTableau_row_int - \l_tmpa_int } { \seq_put_right:Nx \l__aTableau_skew_seq {\int_eval:n{\l_tmpa_int+##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_start_ribbon_path:nnnn r1|c1|r2|c2 % Start a (new connected component of) the path around the boundary of a ribbon % by adding the edge (r1,c1)--(r2,c2) to \l__aTableau_ribbon_path_tl. Set % \l_tmpc_tl to (r2,c2) and set \l__aTableau_adjacent_edges_clist equal to the % list of possible edges in the ribbon boundary that are connected to (r2,c2). % We then search \cs_new_protected:Npn \__aTableau_start_ribbon_path:nnnn #1|#2|#3|#4| { \__aTableau_set_box_coordinates:nnn {a} {#1} {#2} \__aTableau_set_box_coordinates:nnn {b} {#3} {#4} \tl_put_right:Ne \l__aTableau_ribbon_path_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)} \clist_set:Ne \l__aTableau_adjacent_edges_clist { #3|#4|\int_eval:n{#3-1}|#4, \int_eval:n{#3+1}|#4|#3|#4, #3|#4|#3|\int_eval:n{#4-1}, #3|\int_eval:n{#4+1}|#3|#4} \tl_set:Nn \l_tmpc_tl {#3|#4} % last placed coordinate is #3|#4 } % usage: \__aTableau_start_ribbon_path:nnnn r1|c1|r2|c2 % Add the next edge to \l__aTableau_ribbon_path_tl in a (connected component % of) the path around the boundary of a ribbon. To determine whether the next % vertex is (r1,c1) or (r2,c2) we look at \l_tmpc_tl, which is the last vertex % that was added. Finally, update \l_tmpc_tl to point to the last vertex, and % set \l__aTableau_adjacent_edges_clist equal to the list of possible adjacent % edges that care connected to \l_tmpc_tl. \cs_new_protected:Npn \__aTableau_add_to_ribbon_path:nnnn #1|#2|#3|#4| { \tl_if_eq:VnTF \l_tmpc_tl {#1|#2} { % the last placed coordinate was #1|#2, so the new vertex is #3|#4 \__aTableau_set_box_coordinates:nnn {a} {#3} {#4} \tl_put_right:Ne \l__aTableau_ribbon_path_tl {--(\fp_use:N\l__aTableau_xa_fp,\fp_use:N\l__aTableau_ya_fp)} \tl_set:Nn \l_tmpc_tl {#3|#4} % last vertex in boundary is #3|#4 % create list of the edges that can connect to \l_tmpc_tl \clist_set:Ne \l__aTableau_adjacent_edges_clist { #3|#4|\int_eval:n{#3-1}|#4, \int_eval:n{#3+1}|#4|#3|#4, #3|#4|#3|\int_eval:n{#4-1}, #3|\int_eval:n{#4+1}|#3|#4} } { % the last placed coordinate was #3|#4, so the new vertex is #1|#2 \__aTableau_set_box_coordinates:nnn {a} {#1} {#2} \tl_put_right:Ne \l__aTableau_ribbon_path_tl {--(\fp_use:N\l__aTableau_xa_fp,\fp_use:N\l__aTableau_ya_fp)} \tl_set:Nn \l_tmpc_tl {#1|#2} % last vertex in boundary is #1|#2 % create list of edges that can connect to \l_tmpc_tl \clist_set:Ne \l__aTableau_adjacent_edges_clist { #1|#2|\int_eval:n{#1-1}|#2, \int_eval:n{#1+1}|#2|#1|#2, #1|#2|#1|\int_eval:n{#2-1}, #1|\int_eval:n{#2+1}|#1|#2} } } % usage: \__aTableau_finalise_ribbon: % The edges around the ribbon boundary are stored as keys in \l__aTableau_ribbon_prop. % We pick any edge in the boundary, using \prop_map_inline:Nn, and then add a % connected circuit of edges to \l__aTableau_ribbon_path_tl by searching for % adjacent edges in \l__aTableau_ribbon_prop. As the boundary is a union of % circles, if there are two connected edges it does not matter which edge we % choose. \cs_new_protected:Nn \__aTableau_finalise_ribbon: { \tl_clear:N\l__aTableau_ribbon_path_tl % will become the border of the ribbon \bool_do_while:nn { ! \prop_if_empty_p:N \l__aTableau_ribbon_prop } { % Use \prop_map_inline:Nn to find a key in \l__aTableau_ribbon_prop \prop_map_inline:Nn \l__aTableau_ribbon_prop { \tl_clear:N \l_tmpc_tl % used to determine the next vertex \__aTableau_start_ribbon_path:nnnn ##1| \prop_remove:Ne \l__aTableau_ribbon_prop {##1} % remove the key \prop_map_break: } \bool_set_true:N \l_tmpa_bool % the path corresponding to the key k is k->ribbon_prop[k] -> ribbon_prop[ribbon_prop[k] -> ... \bool_do_while:Nn \l_tmpa_bool { \clist_map_inline:Nn \l__aTableau_adjacent_edges_clist { \bool_set_false:N \l_tmpa_bool \prop_get:NnNT \l__aTableau_ribbon_prop {##1} \l_tmpa_tl { \bool_set_true:N \l_tmpa_bool % found an edge \__aTableau_add_to_ribbon_path:nnnn ##1| \prop_remove:Ne \l__aTableau_ribbon_prop {##1} % remove the key \clist_map_break: } } } } } % 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: { % construct the ribbon boundary in \l__aTableau_ribbon_path_tl \use:c { __aTableau_finalise_ \l__aTableau_ribbon_type_tl :} % draw the ribbon boundary, applying any style \exp_last_unbraced:Ne \draw { [aTableau/\l__aTableau_ribbon_type_tl, \l__aTableau_ribbon_style_tl] } \l__aTableau_ribbon_path_tl; % Now we add the nodes in the ribbon 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 % use ribbon_box, snob_box or path_box \tl_set_eq:Nc \l_tmpc_tl { l__aTableau_\l__aTableau_ribbon_type_tl _box_tl } \seq_map_inline:Nn \l__aTableau_styles_seq { % The last text entry in \l__aTableau_texts_seq will be empty if the % peeking stops before it can be cleared \seq_pop_left:NNF \l__aTableau_texts_seq \l_tmpa_tl {\tl_clear:N\l_tmpa_tl} % pop the text and row and column indices \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:oTF {##1\l_tmpa_tl} { % nodes with default style and no text are added to \l__aTableau_unstyled_nodes_tl \__aTableau_put_tikz_node:neVnV {unstyled_nodes} {aTableau/\l__aTableau_ribbon_type_tl Box,\l__aTableau_ribbon_style_tl} \l__aTableau_name_tl {l} \l_tmpc_tl } { % nodes with styling are added to \l__aTableau_styled_nodes_tl \__aTableau_put_tikz_node:neVnV {styled_nodes} {aTableau/\l__aTableau_ribbon_type_tl Box,\l__aTableau_ribbon_style_tl,##1} \l__aTableau_name_tl {l} \l_tmpa_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_abacus_bead:nnn {style} {coord} {entry} % Draw an abacus bead given its style, coordinates (x#2_fp, y#2_fp), and entry \cs_new_protected:Npn \__aTableau_abacus_bead:nnn #1 #2 #3 { \node[aTableau/abacusBead, #1] at (\fp_use:c{l__aTableau_x#2_fp},\fp_use:c{l__aTableau_y#2_fp}) {\__aTableau_entry:n {#3}}; } % usage: \__aTableau_standard_end:nnnn {style} {initial coordinate} {offset} {±1} % Draw a line for an abacus runner using the initial coordinate (x#2_fp, y#2_fp) -- +(x#3_fp,y#3_fp) \cs_new_protected:Npn \__aTableau_standard_end:nnnn #1 #2 #3 #4 { \draw[aTableau/#1] (\fp_use:c {l__aTableau_x#2_fp},\fp_use:c {l__aTableau_y#2_fp})--+(\fp_use:c {l__aTableau_x#3_fp}, \fp_use:c {l__aTableau_y#3_fp}); } % usage: \__aTableau_standard_runner:nnn {style} {initial coordinate} {offset} % Draw a line for an abacus runner using the initial coordinate (x#2_fp, y#2_fp) -- +(x#3_fp,y#3_fp) \cs_new_protected:Npn \__aTableau_standard_runner:nnn #1 #2 #3 { \draw[aTableau/#1] (\fp_use:c {l__aTableau_x#2_fp}, \fp_use:c {l__aTableau_y#2_fp})--+(\fp_use:c {l__aTableau_x#3_fp}, \fp_use:c{l__aTableau_y#3_fp}); } % usage: \__aTableau_traditional_end:nnnn {style} {initial coordinate} {offset} {±1} % Draw a rod for an abacus runner using the initial coordinate (x#2_fp, y#2_fp) -- +(x#3_fp,y#3_fp) \cs_new_protected:Npn \__aTableau_traditional_end:nnnn #1 #2 #3 #4 { \draw[aTableau/#1] (\fp_use:c{l__aTableau_x#2_fp}, \fp_use:c{l__aTableau_y#2_fp}) rectangle +(\fp_eval:n {\fp_use:c{l__aTableau_x#3_fp}-#4*\l__aTableau_beam_height_fp*\l__aTableau_abacus_row_dx_fp}, \fp_eval:n {\fp_use:c{l__aTableau_y#3_fp}-#4*\l__aTableau_beam_height_fp*\l__aTableau_abacus_row_dy_fp}); } % usage: \__aTableau_traditional_runner:nnn {style} {initial coordinate} {offset} % Draw a rod for an abacus runner using the initial coordinate (x#2_fp, y#2_fp) -- +(x#3_fp,y#3_fp) \cs_new_protected:Npn \__aTableau_traditional_runner:nnn #1 #2 #3 { \shadedraw[aTableau/#1] (\fp_eval:n {\fp_use:c{l__aTableau_x#2_fp}+\l__aTableau_tick_length_fp*\l__aTableau_abacus_col_dx_fp*\l__aTableau_abacus_wd_fp}, \fp_eval:n {\fp_use:c{l__aTableau_y#2_fp}+\l__aTableau_tick_length_fp*\l__aTableau_abacus_col_dy_fp*\l__aTableau_abacus_ht_fp}) rectangle +(\fp_eval:n {\fp_use:c{l__aTableau_x#3_fp}-2*\l__aTableau_tick_length_fp*\l__aTableau_abacus_col_dx_fp*\l__aTableau_abacus_wd_fp}, \fp_eval:n {\fp_use:c{l__aTableau_y#3_fp}-2*\l__aTableau_tick_length_fp*\l__aTableau_abacus_col_dy_fp*\l__aTableau_abacus_ht_fp}); } % usage: \__aTableau_draw_runner_ends:nnnn {symbol} {style} {row index} {±1} \cs_new_protected:Npn \__aTableau_draw_runner_ends:nnnn #1 #2 #3 #4 { \bool_if:NT \l__aTableau_traditional_bool { \tl_if_in:nVF {-_|} #1 { \msg_warning:nnV {aTableau} {unsupported-abacus-end} #1 } } \int_step_inline:nnn {0} {\l__aTableau_cols_int-1} { \seq_if_in:NeF \l__aTableau_dotted_cols_seq { ##1 } { % draw the abacus runners from (xa,ya) to (xb,yb) \__aTableau_set_bead_coordinates:nnn {a} { #3-#4*0.9 } { ##1 } % set the length \fp_set:Nn \l__aTableau_xb_fp {#4*0.9*\l__aTableau_abacus_row_dx_fp*\l__aTableau_abacus_wd_fp} \fp_set:Nn \l__aTableau_yb_fp {#4*0.9*\l__aTableau_abacus_row_dy_fp*\l__aTableau_abacus_ht_fp} \__aTableau_draw_abacus_runner:nnn {clearBoxes} {a} {b} \__aTableau_draw_abacus_runner:nnn {abacusEnds,#2} {a} {b} } } % set default "row height" of the runner labels for use below \fp_set:Nn \l_tmpa_fp {#3-1.4} } % usage: \__aTableau_add_abacus_end:Vnn {abacus_top/abacus_bottom} {row index} {±1} % Draw the top/bottom on the abacus. Here: % - #1 is either % \l__aTableau_abacus_top_tl or \l__aTableau_abacus_bottom_tl % - #2 is the row index for the line we will draw % - #3, which is ±1, determines whether arrows go up or down % either 0, for top, or the row index of the last row, for bottom \cs_new_protected:Npn \__aTableau_add_abacus_end:Vnn #1 #2 #3 { \str_case:Vn #1 { {-} % draw a line from the just before the first runner to just after the last runner { \__aTableau_set_bead_coordinates:nnn {a} {#2} {-0.5} \fp_set:Nn \l__aTableau_xb_fp {\l__aTableau_cols_int*\l__aTableau_abacus_col_dx_fp*\l__aTableau_abacus_wd_fp} \fp_set:Nn \l__aTableau_yb_fp {\l__aTableau_cols_int*\l__aTableau_abacus_col_dy_fp*\l__aTableau_abacus_ht_fp} \__aTableau_draw_abacus_end:nnnn {abacusEnds} {a} {b} {#3} % set default "row height" of the runner labels for use below \fp_set:Nn \l_tmpa_fp {#2-0.4} } {_} % draw a line from the first to last runner { \bool_if:NTF \l__aTableau_traditional_bool { \__aTableau_set_bead_coordinates:nnn {a} {#2} {-\l__aTableau_tick_length_fp} \fp_set:Nn \l__aTableau_xb_fp {(\l__aTableau_cols_int-1+2*\l__aTableau_tick_length_fp)*\l__aTableau_abacus_col_dx_fp*\l__aTableau_abacus_wd_fp} \fp_set:Nn \l__aTableau_yb_fp {(\l__aTableau_cols_int-1+2*\l__aTableau_tick_length_fp)*\l__aTableau_abacus_col_dy_fp*\l__aTableau_abacus_ht_fp} } { \__aTableau_set_bead_coordinates:nnn {a} {#2} {0} \fp_set:Nn \l__aTableau_xb_fp {(\l__aTableau_cols_int-1)*\l__aTableau_abacus_col_dx_fp*\l__aTableau_abacus_wd_fp} \fp_set:Nn \l__aTableau_yb_fp {(\l__aTableau_cols_int-1)*\l__aTableau_abacus_col_dy_fp*\l__aTableau_abacus_ht_fp} } \__aTableau_draw_abacus_end:nnnn {abacusEnds} {a} {b} {#3} % set default "row height" of the runner labels for use below \fp_set:Nn \l_tmpa_fp {#2-0.4} } {.} % draw dots ... { \__aTableau_draw_runner_ends:nnnn {#1} {dotted} {#2} {#3} } {>} % draw arrow --> except for the dotted columns { \__aTableau_draw_runner_ends:nnnn {#1} {<-} {#2} {#3} } {*} % draw dotted arrow ...-> { \__aTableau_draw_runner_ends:nnnn {#1} {dotted,<-} {#2} {#3} } {|} {} % no extra decoration => do nothing } } % usage: \__aTableau_abacus_row_labels: % Add nodes containing the row labels \cs_new_protected:Nn \__aTableau_abacus_row_labels: { % add the runner labels using the "row height" #1, which is set by \__aTableau_draw_abacus_end:Vnn \int_zero:N \l__aTableau_r_int \seq_map_inline:Nn \l__aTableau_row_labels_seq { \__aTableau_set_bead_coordinates:nVn {a} \l__aTableau_r_int {-1} \seq_if_in:NVF \l__aTableau_dotted_rows_seq \l__aTableau_r_int { \node[aTableau/rowLabel] at (\fp_use:N\l__aTableau_xa_fp,\fp_use:N\l__aTableau_ya_fp){ \__aTableau_entry:n{##1} }; } \int_incr:N \l__aTableau_r_int } } % usage: \__aTableau_abacus_runner_labels:n { height } % Add nodes containing the runner labels \cs_new_protected:Npn \__aTableau_abacus_runner_labels:n #1 { % add the runner labels using the "row height" #1, which is set by \__aTableau_draw_abacus_end:Vnn \int_zero:N \l__aTableau_c_int \seq_map_inline:Nn \l__aTableau_runner_labels_seq { \__aTableau_set_bead_coordinates:nnV {a} {#1} \l__aTableau_c_int \seq_if_in:NVF \l__aTableau_dotted_cols_seq \l__aTableau_c_int { \node[aTableau/runnerLabel] 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 } } % usage: \__aTableau_extend_beta_numbers: % This is a helper function to when parsing gthe abacus bead specifications. It % extends the length of the partition, or sequence of beta numbers, if it is % shorter than \l__aTableau_beads_int by appropriately modifying the sequences % \l__aTableau_rcs_seq, \l__aTableau_texts_seq and \l__aTableau_styles_seq \cs_new_protected:Nn \__aTableau_extend_beta_numbers: { \tl_set:No \l_tmpa_tl { \seq_count:N \l__aTableau_rcs_seq } \int_compare:nNnTF {\l__aTableau_beads_int} > {\l_tmpa_tl} { \bool_if:nTF \l__aTableau_beta_numbers_bool { % need to increment the existing beta numbers \seq_set_eq:NN \l_tmpa_seq \l__aTableau_rcs_seq \int_set:Nn \l_tmpa_int { \l__aTableau_beads_int-\l_tmpa_tl } \seq_clear:N \l__aTableau_rcs_seq \seq_map_inline:Nn \l_tmpa_seq { \seq_put_right:Ne \l__aTableau_rcs_seq { \int_eval:n {##1+\l_tmpa_int}} } % now need to add some more beads \int_step_inline:nnnn {\l_tmpa_int-1} (-1) {0} { \seq_put_right:Nn \l__aTableau_rcs_seq {##1} \seq_put_right:Nn \l__aTableau_texts_seq {} \seq_put_right:Nn \l__aTableau_styles_seq {} } } { % need to add extra zeros to the partition \int_step_inline:nnn {\l_tmpa_tl} {\l__aTableau_beads_int-1} { \seq_put_right:Nn \l__aTableau_rcs_seq {0} \seq_put_right:Nn \l__aTableau_texts_seq {} \seq_put_right:Nn \l__aTableau_styles_seq {} } } } { % give a warning if there are more beads than l__aTableau_beads_int \bool_if:nT { \int_compare_p:nNn {\l__aTableau_beads_int} > {0} && \int_compare_p:nNn {\l__aTableau_beads_int} < {\l_tmpa_tl} } { \msg_warning:nne {aTableau} {many-beads} \l_tmpa_tl \int_set:NV \l__aTableau_beads_int \l_tmpa_tl } } } % usage: \__aTableau_abacus:nn { #runners } { bead specifications } % This is the main routine for the \Abacus command. It parses the abacus % specifications by calling the peeking functions above, then draws the % runners, frame and beads \cs_new_protected:Npn \__aTableau_abacus:nn #1 #2 { % set the star style \tl_set:Nn \l__aTableau_starstyle_tl {aTableau/abacusStar} % record the number of runners in cols_int \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 hold the beta numbers \seq_clear:N \l__aTableau_styles_seq % the bead style \seq_clear:N \l__aTableau_texts_seq % the bead text \int_zero:N \l__aTableau_row_int % the maximum beta number % Read the partition/beta numbers and the style settings. The beta numbers % for the abacus are first placed in \l__aTableau_rcs_seq by the peeking % routines, after which they are converted to beta numbers, if needed, and % stored in \l__aTableau_shape_seq % look for a | to see if we have been given the partition/beta numbers as a quotient \tl_if_in:nnTF {#2} {|} { % abacus is specified as |-separated list that gives the quotient % store the components in \l__aTableau_component_seq \seq_set_split:Nnn \l__aTableau_component_seq {|} {#2} % check that we do not have too many components \int_compare:oNnT { \seq_count:N \l__aTableau_component_seq } > {\l__aTableau_cols_int} { \msg_error:nnx {aTableau} { quotient-components } { \seq_count:N \l__aTableau_component_seq > #1 } } \int_zero:N \l__aTableau_c_int % offset for each runner \seq_map_inline:Nn \l__aTableau_component_seq { \seq_clear:N \l__aTableau_rcs_seq \tl_if_blank:nF {##1} { % add the entries in the quotient, peeking between commas \__aTableau_peek_style:nw {record_style:nn} ##1 \q_recursion_tail \q_recursion_stop % Extend the length of the partition, or sequence of beta numbers, if % it is shorter than \l__aTableau_beads_int \__aTableau_extend_beta_numbers: % convert the sequence in \l__aTableau_rcs_seq into beta numbers and add to \l__aTableau_shape_seq \bool_if:NTF \l__aTableau_beta_numbers_bool { % entered as beta numbers, so just need to multiply by e = \l__aTableau_cols_int \int_set:No \l_tmpa_int { \l__aTableau_cols_int * (\seq_item:Nn \l__aTableau_rcs_seq {1})} % maximum beta number \seq_map_inline:Nn \l__aTableau_rcs_seq { \seq_put_right:Ne \l__aTableau_shape_seq { \int_eval:n {\l__aTableau_c_int+\l__aTableau_cols_int*####1} } } } { % convert the partition to beta numbers \tl_set:No \l_tmpa_tl {\seq_count:N \l__aTableau_rcs_seq} % number of beads \int_set:Nn \l__aTableau_r_int {\l_tmpa_tl-1}% beta number offset for each part \tl_set:No \l_tmpb_tl { \seq_item:Nn \l__aTableau_rcs_seq {1} } % first part in quotient \int_set:Nn \l_tmpa_int { \l__aTableau_c_int+\l__aTableau_cols_int*(\l__aTableau_r_int+\l_tmpb_tl) } % maximum beta number \seq_map_inline:Nn \l__aTableau_rcs_seq { \seq_put_right:Ne \l__aTableau_shape_seq { \int_eval:n { \l__aTableau_c_int+\l__aTableau_cols_int*(####1+\l__aTableau_r_int) } } \int_decr:N \l__aTableau_r_int } } % check that the maximum beta number has not changed \int_compare:oNnT { \l_tmpa_int } > { \l__aTableau_row_int } { \int_set_eq:NN \l__aTableau_row_int \l_tmpa_int } } % increment runner offset for next component \int_incr:N \l__aTableau_c_int } \int_set:No \l__aTableau_beads_int { \seq_count:N \l__aTableau_shape_seq } } { % abacus is specified by a partition/sequence of beta numbers % Parse #2 into the rows and columns with style. Initially we used % \seq_set_split:Nnn here, but this required escaping complex style % specifications as [{...}]. Now we peek for the commas and the style % simultaneously. \seq_clear:N \l__aTableau_rcs_seq % initially store beta numbers here and then move to \l__aTableau_shape_seq \tl_if_blank:nF {#2} { \__aTableau_peek_style:nw {record_style:nn} #2 \q_recursion_tail \q_recursion_stop % Extend the length of the partition, or sequence of beta numbers, if % it is shorter than \l__aTableau_beads_int \__aTableau_extend_beta_numbers: \int_set:No \l__aTableau_beads_int { \seq_count:N \l__aTableau_rcs_seq } % convert \l__aTableau_rcs_seq to beta numbers in \l__aTableau_shape_seq \bool_if:NTF \l__aTableau_beta_numbers_bool { \seq_set_eq:NN \l__aTableau_shape_seq \l__aTableau_rcs_seq } { \int_set:Nn \l__aTableau_r_int {\l__aTableau_beads_int-1} % the offset to the beta numbers in each row \seq_map_inline:Nn \l__aTableau_rcs_seq { \seq_put_right:Ne \l__aTableau_shape_seq { \int_eval:n { ##1 + \l__aTableau_r_int } } \int_decr:N \l__aTableau_r_int } } % set the maximum beta number \int_set:No \l__aTableau_row_int { 0\seq_item:Nn \l__aTableau_shape_seq {1}} } } % determine the number of abacus rows required \int_set:Nn \l_tmpa_int { 1+\int_div_truncate:nn {\l__aTableau_row_int} {#1} } \int_compare:nNnT {\l__aTableau_rows_int} < { \l_tmpa_int } { % give a warning if we need more rows than \l__aTableau_rows_int \int_compare:nNnT {\l__aTableau_rows_int} > {0} { \msg_warning:nne {aTableau} {many-rows} {\int_use:N\l_tmpa_int} } % set the number of rows \int_set_eq:NN \l__aTableau_rows_int \l_tmpa_int } % draw the abacus runners \int_zero:N \l__aTableau_col_int % compute the change in x and y coordinates when we move to the end of the runner, % which we store as (xl_fp, yl_fp) for use in \__aTableau_draw_abacus_runner:nnn \fp_set:Nn \l_tmpa_fp {\l__aTableau_abacus_top_fp+\l__aTableau_rows_int+\l__aTableau_abacus_bottom_fp-0.5} \fp_set:Nn \l__aTableau_xl_fp { \l_tmpa_fp*\l__aTableau_abacus_row_dx_fp*\l__aTableau_abacus_wd_fp } \fp_set:Nn \l__aTableau_yl_fp { \l_tmpa_fp*\l__aTableau_abacus_row_dy_fp*\l__aTableau_abacus_ht_fp } % similarly, set (xb_fp, yb_fp) to the half the tick length \fp_set:Nn \l__aTableau_xb_fp {\l__aTableau_tick_length_fp*\l__aTableau_abacus_col_dx_fp} \fp_set:Nn \l__aTableau_yb_fp {\l__aTableau_tick_length_fp*\l__aTableau_abacus_col_dy_fp} \int_step_inline:nnn {0} {\l__aTableau_cols_int-1} { % skip the runners in dotted_cols_seq \seq_if_in:NnF \l__aTableau_dotted_cols_seq {##1} { % draw the abacus runners from (xa,ya) to +(xl,yl) \__aTableau_set_bead_coordinates:nnn {a} {-\l__aTableau_abacus_top_fp} {##1} \__aTableau_draw_abacus_runner:nnn {abacusRunner} {a} {l} % draw ticks \int_step_inline:nnn {0} {\l__aTableau_rows_int-1} { \__aTableau_set_bead_coordinates:non {a} { ####1 } {##1} % add a named node first that sits on the runner \node[aTableau/namedTick] (\l__aTableau_name_tl) at (\fp_use:N\l__aTableau_xa_fp,\fp_use:N\l__aTableau_ya_fp){}; % draw the tick, which extends to either side of the runner \draw[aTableau/abacusTick] (\fp_eval:n{\l__aTableau_xa_fp+\l__aTableau_xb_fp},\fp_eval:n{\l__aTableau_ya_fp+\l__aTableau_yb_fp}) --(\fp_eval:n{\l__aTableau_xa_fp-\l__aTableau_xb_fp},\fp_eval:n{\l__aTableau_ya_fp-\l__aTableau_yb_fp}); } } } \bool_if:NT \l__aTableau_framed_bool { % draw extra runners at the left and right when framed is true \__aTableau_set_bead_coordinates:nnn {b} {-\l__aTableau_abacus_top_fp} {\l__aTableau_cols_int-0.4} \__aTableau_set_bead_coordinates:nnn {a} {-\l__aTableau_abacus_top_fp} {-0.6} \bool_if:NTF \l__aTableau_traditional_bool { % need to move the ends up by \l__aTableau_beam_height_fp \fp_add:Nn \l__aTableau_xa_fp {-\l__aTableau_beam_height_fp*\l__aTableau_abacus_row_dx_fp} \fp_add:Nn \l__aTableau_ya_fp {-\l__aTableau_beam_height_fp*\l__aTableau_abacus_row_dy_fp} \fp_add:Nn \l__aTableau_xb_fp {-\l__aTableau_beam_height_fp*\l__aTableau_abacus_row_dx_fp} \fp_add:Nn \l__aTableau_yb_fp {-\l__aTableau_beam_height_fp*\l__aTableau_abacus_row_dy_fp} % need to make the runners slightly longer \fp_add:Nn \l__aTableau_xl_fp {2*\l__aTableau_beam_height_fp*\l__aTableau_abacus_row_dx_fp} \fp_add:Nn \l__aTableau_yl_fp {2*\l__aTableau_beam_height_fp*\l__aTableau_abacus_row_dy_fp} } { \fp_set:Nn \l_tmpb_fp {-\l__aTableau_abacus_top_fp} } % draw the extra runners \__aTableau_draw_abacus_runner:nnn {abacusRunner} {a} {l} \__aTableau_draw_abacus_runner:nnn {abacusRunner} {b} {l} % use the wide abacus ends \__aTableau_set_abacus_ends:nn -- } % draw the top of the abacus \__aTableau_add_abacus_end:Vnn \l__aTableau_abacus_top_tl {-\l__aTableau_abacus_top_fp} {1} % add any abacus runner labels \bool_if:NT \l__aTableau_traditional_bool { \fp_add:Nn \l_tmpa_fp {-\l__aTableau_beam_height_fp/\l__aTableau_abacus_ht_fp} } \__aTableau_abacus_runner_labels:n \l_tmpa_fp % add any abacus row labels \__aTableau_abacus_row_labels: % draw the bottom of the abacus \__aTableau_add_abacus_end:Vnn \l__aTableau_abacus_bottom_tl {\l__aTableau_rows_int+\l__aTableau_abacus_bottom_fp-0.5} {-1} % 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:nNnF {\l__aTableau_e_int} < {1000} { \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} } } } } % for certain entries, we need the beta numbers to be correctly sorted, so we set this up now \tl_if_blank:VF \l__aTableau_show_tl { % put the beta numbers into \l__aTableau_rcs_seq and sort them \seq_set_eq:NN \l__aTableau_rcs_seq \l__aTableau_shape_seq \seq_sort:Nn \l__aTableau_rcs_seq { % put into decreasing order \int_compare:nNnTF { ##1 } < { ##2 } { \sort_return_swapped: } { \sort_return_same: } } } % draw the beads on the abacus last so that they are on top \int_step_inline:nn { \l__aTableau_beads_int } % finally, add the beads, with labels and styles { \int_set:No \l_tmpa_int { \seq_item:Nn \l__aTableau_shape_seq {##1} } % 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:nVV {l} \l__aTableau_row_int \l__aTableau_col_int % add the bead only if they are not in a dotted row or column \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 { % check to see if we need to show something for this entry \str_case:VnF \l__aTableau_show_tl { {beads} { \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} { % find the correct index of the beta number \l_tmpa_int in \int_zero:N \l__aTableau_r_int \tl_set:No \l_tmpa_tl {\int_use:N \l_tmpa_int} \seq_map_inline:Nn \l__aTableau_rcs_seq { \int_incr:N \l__aTableau_r_int \tl_if_eq:VnT \l_tmpa_tl {####1} { \int_set:Nn \l_tmpb_int { \l_tmpa_int+\l__aTableau_r_int-\l__aTableau_beads_int } % corresponding part \int_compare:nNnTF {\l_tmpb_int} > {0} { \tl_set:No \l_tmpa_tl { \int_use:N \l__aTableau_r_int } } { \tl_set:No \l_tmpa_tl { {-} } } \seq_map_break: } } } {shape} { % find the correct index of the beta number \l_tmpa_int in \int_zero:N \l__aTableau_r_int \tl_set:No \l_tmpa_tl {\int_use:N \l_tmpa_int} \seq_map_inline:Nn \l__aTableau_rcs_seq { \int_incr:N \l__aTableau_r_int \tl_if_eq:VnT \l_tmpa_tl {####1} { \tl_set:No \l_tmpa_tl { \int_eval:n {\l_tmpa_int+\l__aTableau_r_int-\l__aTableau_beads_int } }% corresponding part \seq_map_break: } } } { } { \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} } \__aTableau_abacus_bead:enV \l_tmpb_tl {l} \l_tmpa_tl } } } % 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_abacus_row_dx_fp*\l__aTableau_abacus_wd_fp} \fp_set:Nn \l_tmpb_fp {\l__aTableau_abacus_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_abacus_col_dx_fp*\l__aTableau_abacus_wd_fp} \fp_set:Nn \l__aTableau_ya_fp {\l__aTableau_yl_fp-0.35*\l__aTableau_abacus_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_abacus_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_abacus_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 ; % replace line 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_zero:N \l__aTableau_row_int \int_step_inline:nn {\l__aTableau_rows_int-1} { \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_abacus_col_dx_fp*\l__aTableau_abacus_wd_fp} \fp_set:Nn \l_tmpb_fp {\l__aTableau_abacus_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:nnn {l} { \l__aTableau_row_int } { 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_abacus_row_dx_fp*\l__aTableau_abacus_wd_fp} \fp_set:Nn \l__aTableau_ya_fp {\l__aTableau_yl_fp-0.35*\l__aTableau_abacus_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_abacus_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_abacus_row_dy_fp*\l__aTableau_abacus_ht_fp} \draw[aTableau/clearBoxes] (\fp_eval:n{\l__aTableau_xa_fp-0.12*\l_tmpa_fp-\l__aTableau_tick_length_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-\l__aTableau_tick_length_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-\l__aTableau_tick_length_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-\l__aTableau_tick_length_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); } } } % --------------------------------------------------------------------------- % Define keys for the aTableau options and set 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 } { % --------------------------------------------------------------------------- % print an error for non-empty unknown keys unknown .code:n = { \tl_if_empty:nF {#1} { \msg_error:nnn {aTableau} {invalid-key} {#1} } }, % --------------------------------------------------------------------------- % general settings % picture 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:nnn { aTableau } { unknown-baseline } {#1} }, align .initial:n = centre, % set the beamer command \only, \uncover, ... beamer .choices:nn = { invisible, only, onslide, uncover, visible }{ \tl_set:Nn \l__aTableau_beamer_tl {#1} }, beamer .unknown .code:n = { \msg_error:nne { aTableau } { unknown-beamer } {#1} }, beamer .value_required:n = true, beamer .initial:n = only, % 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, % change the colour theme colour~theme .choices:nn = { default, classic, natural } { \clist_map_inline:nn {Main,Inner,Skew,Fill,Star,Rod} { \__aTableau_define_html_colour:no {####1} { \prop_item:Nn \l__aTableau_colour_theme_prop {#1_####1} } } }, colour~theme/unknown .code:n = { \msg_error:nnx {aTableau} {unknown-theme} {#1} }, colour~theme .initial:n = default, color~theme .meta:n = { colour~theme={#1} }, color~theme .value_required:n = true, % dotted rows and columns for tableaux and abacuses dotted~cols .code:n = { \__aTableau_set_multi_seq_key:nn {dotted_cols} {#1} }, dotted~cols .value_required:n = true, dotted~rows .code:n = { \__aTableau_set_multi_seq_key:nn {dotted_rows} {#1} }, dotted~rows .value_required:n = true, e .int_set:N = \l__aTableau_e_int, e .initial:n = 1000, % an arbitrary large default 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~boxes .code:n = { \cs_set_eq:NN \__aTableau_entry:n \__aTableau_entry_math:n }, text~boxes .code:n = { \cs_set_eq:NN \__aTableau_entry:n \__aTableau_entry_text:n }, % for compatibility with v2.1.0 we still allow 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, % rotating coordinate systems rotate .code:n = { \fp_set:Nn \l__aTableau_rotate_fp {#1} \cs_set_eq:NN \__aTableau_box:n \__aTableau_rotated_box:n \fp_set:Nn \l_tmpa_fp { cosd(#1) } % cos(#1), with #1 in degrees \fp_set:Nn \l_tmpb_fp { sind(#1) } % sin(#1), with #1 in degrees % rotate the tableau coordinates \fp_set:Nn \l__aTableau_xa_fp {\l_tmpa_fp*\l__aTableau_box_col_dx_fp-\l_tmpb_fp*\l__aTableau_box_col_dy_fp} \fp_set:Nn \l__aTableau_box_col_dy_fp {\l_tmpb_fp*\l__aTableau_box_col_dx_fp+\l_tmpa_fp*\l__aTableau_box_col_dy_fp} \fp_set_eq:NN \l__aTableau_box_col_dx_fp \l__aTableau_xa_fp \fp_set:Nn \l__aTableau_xa_fp {\l_tmpa_fp*\l__aTableau_box_row_dx_fp-\l_tmpb_fp*\l__aTableau_box_row_dy_fp} \fp_set:Nn \l__aTableau_box_row_dy_fp {\l_tmpb_fp*\l__aTableau_box_row_dx_fp+\l_tmpa_fp*\l__aTableau_box_row_dy_fp} \fp_set_eq:NN \l__aTableau_box_row_dx_fp \l__aTableau_xa_fp % rotate the abacus coordinates \fp_set:Nn \l__aTableau_xa_fp {\l_tmpa_fp*\l__aTableau_abacus_col_dx_fp-\l_tmpb_fp*\l__aTableau_abacus_col_dy_fp} \fp_set:Nn \l__aTableau_abacus_col_dy_fp {\l_tmpb_fp*\l__aTableau_abacus_col_dx_fp+\l_tmpa_fp*\l__aTableau_abacus_col_dy_fp} \fp_set_eq:NN \l__aTableau_abacus_col_dx \l__aTableau_xa_fp \fp_set:Nn \l__aTableau_xa_fp {\l_tmpa_fp*\l__aTableau_abacus_row_dx_fp-\l_tmpb_fp*\l__aTableau_abacus_row_dy_fp} \fp_set:Nn \l__aTableau_abacus_row_dy_fp {\l_tmpb_fp*\l__aTableau_abacus_row_dx_fp+\l_tmpa_fp*\l__aTableau_abacus_row_dy_fp} \fp_set_eq:NN \l__aTableau_abacus_row_dx \l__aTableau_xa_fp }, rotate .value_required:n = true, % scaling scale .code:n = { \__aTableau_set_xscale:n {#1} \__aTableau_set_yscale:n {#1} }, scale .value_required:n = true, % subscript and subsubscript scaling script .fp_set:N = \l__aTableau_script_fp, script .value_required:n = true, script .initial:n = 0.5, scriptscript .fp_set:N = \l__aTableau_scriptscript_fp, scriptscript .value_required:n = true, scriptscript .initial:n = 0.4, tableau~star .code:n = { \__aTableau_tikzset_append:nn {tableauStar} {#1} }, tableau~star .value_required:n = true, star~style .meta:n = {tableau~star={#1}}, % depreciating % 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_tableau_style_australian:, australian .code:n = \__aTableau_set_tableau_style_australian:, australian .value_required:n = false, English .code:n = \__aTableau_set_tableau_style_english:, english .code:n = \__aTableau_set_tableau_style_english:, english .value_required:n = false, english .initial:n =, % default style French .code:n = \__aTableau_set_tableau_style_french:, french .code:n = \__aTableau_set_tableau_style_french:, french .value_required:n = false, ukrainian .code:n = \__aTableau_set_tableau_style_ukrainian:, Ukrainian .code:n = \__aTableau_set_tableau_style_ukrainian:, ukrainian .value_required:n = false, Russian .code:n = \__aTableau_set_tableau_style_ukrainian:, russian .code:n = \__aTableau_set_tableau_style_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 {border} {#1} }, border~style .value_required:n = true, % node height and width box~height .code:n = { \fp_set:Nn \l__aTableau_box_col_dy_fp { \l__aTableau_box_col_dy_fp*#1/\l__aTableau_box_ht_fp} \fp_set:Nn \l__aTableau_box_row_dy_fp { \l__aTableau_box_row_dy_fp*#1/\l__aTableau_box_ht_fp} \fp_set:Nn \l__aTableau_box_ht_fp {#1} }, box~height .value_required:n = true, box~height .initial:n = 0.5, box~width .code:n = { \fp_set:Nn \l__aTableau_box_col_dx_fp { \l__aTableau_box_col_dx_fp*#1/\l__aTableau_box_wd_fp} \fp_set:Nn \l__aTableau_box_row_dx_fp { \l__aTableau_box_row_dx_fp*#1/\l__aTableau_box_wd_fp} \fp_set:Nn \l__aTableau_box_wd_fp {#1} }, 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 {tableauBox} {#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, colours .code:n = \__aTableau_set_colours:n {#1}, colors .code:n = \__aTableau_set_colours:n {#1}, colours .value_required:n = true, colors .value_required:n = true, conjugate .code:n = { % change to using conjugate box coordinates \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 = { \__aTableau_set_multi_tl_key:nn {label} {#1} }, label~style .code:n = { \__aTableau_tikzset_append:nn {label} {#1} }, label~style .value_required:n = true, % -- paths --------------------------------------- paths .code:n = { \__aTableau_set_multi_tl_key:nn {paths} {#1} }, paths .initial:n = , path~style .code:n = { \__aTableau_tikzset_append:nn {path} {#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_multi_tl_key:nn {ribbons} {#1} }, ribbons .initial:n = , ribbon~style .code:n = { \__aTableau_tikzset_append:nn {ribbon} {#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 =, shape .code:n = { \__aTableau_set_partition:nn {shape} {#1} }, % -- snobs --------------------------------------- snobs .code:n = { \__aTableau_set_multi_tl_key:nn {snobs} {#1} }, snobs .initial:n = , snob~style .code:n = { \__aTableau_tikzset_append:nn {snob} {#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 ------------ shifted .bool_set:N = \l__aTableau_shifted_bool, shifted .initial:n = false, % -- cover settings ------------ cover .code:n = \__aTableau_set_multi_shape_key:nn {cover} {#1}, cover .value_required:n = true, cover .initial:n =, cover~border .bool_set:N = \l__aTableau_cover_border_bool, cover~border .default:n = true, cover~border .initial:n = true, no~cover~border .bool_set_inverse:N = \l__aTableau_cover_border_bool, no~cover~border .default:n = true, cover~border~style .code:n = { \__aTableau_tikzset_append:nn {coverBorder} {#1} }, cover~border~style .value_required:n = true, cover~boxes .bool_set:N = \l__aTableau_cover_boxes_bool, cover~boxes .default:n = true, cover~boxes .initial:n = true, no~cover~boxes .bool_set_inverse:N = \l__aTableau_cover_boxes_bool, cover~box~style .code:n = { \__aTableau_tikzset_append:nn {coverBox} {#1} }, cover~box~style .value_required:n = true, cover colour .tl_set:N = \l__aTableau_cover_border_tl, cover color .tl_set:N = \l__aTableau_cover_border_tl, cover colour .initial:n = aTableauSkew, % -- skew settings ------------ skew .code:n = \__aTableau_set_multi_shape_key: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, skew~border .default:n = true, 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 shapes ------------ 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 = -, 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_abacus_style:nnnn {1}{0}{0}{-1}, south .value_required:n = false, south .initial:n =, % use south by default east .code:n = \__aTableau_set_abacus_style:nnnn {0}{-1}{1}{0}, east .value_required:n = false, north .code:n = \__aTableau_set_abacus_style:nnnn {1}{0}{0}{1}, north .value_required:n = false, west .code:n = \__aTableau_set_abacus_style:nnnn {0}{1}{-1}{0}, west .value_required:n = false, % 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~bottom .fp_set:N = \l__aTableau_abacus_bottom_fp, abacus~bottom .value_required:n = true, abacus~bottom .initial:n = 0.1, abacus~top .code:n = \fp_set:Nn \l__aTableau_abacus_top_fp {1-#1}, abacus~top .value_required:n = true, abacus~top .initial:n = 0.4, abacus~star .code:n = { \__aTableau_tikzset_append:nn {abacusStar} {#1} }, abacus~star .value_required:n = true, abacus~star~style .meta:n = {abacus~star={#1}}, % depreciating 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~height .fp_set:N = \l__aTableau_bead_height_fp, bead~height .value_required:n = true, bead~height .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~size .code:n = { \fp_set:Nn \l__aTableau_bead_height_fp {#1} \fp_set:Nn \l__aTableau_bead_width_fp {#1} }, bead~size .value_required:n = true, bead~style .code:n = { \__aTableau_tikzset_append:nn {abacusBead} {#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, bead~width .fp_set:N = \l__aTableau_bead_width_fp, bead~width .value_required:n = true, bead~width .initial:n = 0.4, beads .int_set:N = \l__aTableau_beads_int, % number of beads on the abacus beads .initial:n = 0, beads.value_required:n = true, beam~height .fp_set:N = \l__aTableau_beam_height_fp, % beam height beam~height .value_required:n = true, beam~height .initial:n = 0.13, beta~numbers .bool_set:N = \l__aTableau_beta_numbers_bool, beta~numbers .default:n = true, beta~numbers .initial:n = false, framed .bool_set:N = \l__aTableau_framed_bool, framed .default:n = true, framed .initial:n = false, row~labels .code:n = { \seq_set_split:Nnn \l__aTableau_row_labels_seq {,} {#1} }, row~labels .value_required:n = true, row~label~style .code:n = { \__aTableau_tikzset_append:nn {rowLabel} {#1} }, row~label~style .value_required:n = true, runner .tl_set:N = \l__aTableau_runner_tl, % runner colour runner .value_required:n = true, runner~style .code:n = { \__aTableau_tikzset_append:nn {abacusRunner} {#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 {runnerLabel} {#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 {abacusTick} {#1} }, tick~style .value_required:n = true, traditional .code:n = { \bool_if:cTF {c_#1_bool} { \__aTableau_set_abacus_convention_traditional: } { \__aTableau_set_abacus_convention_standard: } }, traditional .value_required:n = false, traditional .default:n = true, traditional .initial:n = false, unshaded .code:n = { \__aTableau_tikzset_append:nn {abacusBead} {unshaded} }, unshaded .value_required:n = false, } % --------------------------------------------------------------------------- % Usage: \__aTableau_set_origin:nn (x,y) % Set the Cartesian coordinates for the corner of the (1,1) box % TODO: allow general TikZ-coordinates. 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 {origin} {aTableau command} % - #1: origin are Cartesian coordinates in the form x,y, or NoValue % - #2: an internal aTableau command with parameters for drawing something % Except for \aTabset and \NewATableauCommand, all public-facing commands % use \__aTableau_tikzpicture:nnn, which ensures that the command is % run inside a tikzpicture environment \cs_new_protected:Npn \__aTableau_tikzpicture:nn #1 #2 { \IfNoValueTF {#1} { % Wrap inside a tikzpicture environment, placing the picture at (0,0). % Checking for the existence of the \draw command is a proxy for % determining whether we are inside a tikzpicture environment since % it is only defined when we are \if_cs_exist:N \draw \msg_error:nn {aTableau} { inside-tikzpicture} \fi: \__aTableau_set_origin:nn (0,0) \mode_if_math:TF { % We want to automatically rescale when used as a subscript, which % we do by putting the tikz code inside a box and then using % \mathchoice to adjust for script size, using an idea of cfr's \hbox_set:Nw \l_tmpa_box \exp_last_unbraced:Ne \tikz{[\l__aTableau_tikzpicture_tl]} { \l__aTableau_tikz_before_tl #2 \l__aTableau_tikz_after_tl } \hbox_set_end: \mathchoice { % display style: do nothing \box_use:N \l_tmpa_box } { % text style: do nothing \box_use:N \l_tmpa_box } { % script style => rescale using \l__aTableau_script_fp \box_scale:Nnn \l_tmpa_box {\l__aTableau_script_fp*\l__aTableau_xscale_fp} {\l__aTableau_script_fp*\l__aTableau_yscale_fp} \box_use:N \l_tmpa_box } { % scriptscript style => rescale using \l__aTableau_scriptscript_fp \box_scale:Nnn \l_tmpa_box {\l__aTableau_scriptscript_fp*\l__aTableau_xscale_fp} {\l__aTableau_scriptscript_fp*\l__aTableau_yscale_fp} \box_use:N \l_tmpa_box } } { % Not in maths-mode. In the manual, tcolorbox objects to using a box here \exp_last_unbraced:Ne \tikz{[\l__aTableau_tikzpicture_tl]} { \l__aTableau_tikz_before_tl #2 \l__aTableau_tikz_after_tl } } } { % already inside a tikzpicture environment \if_cs_exist:N \draw \else: \msg_error:nn {aTableau} { outside-tikzpicture} \fi: \__aTableau_set_origin:nn (#1) \l__aTableau_tikz_before_tl #2 \l__aTableau_tikz_after_tl } } % The next two wrapper commands apply the settings inside a group, to keep % changes local. They then call \__aTableau_tikzpicture:nn to ensure that the % aTableau command is run inside a tikzpicture environment. We do it this way % so that the commands are beamer enabled only if beamer is being used. We call % the settings below so that the aTableau settings can change the beamer % overlay command that is used. % usage: \__aTableau_command_wrapper:nnn {settings} {origin} {aTableau command} \cs_new_protected:Npn \__aTableau_command_wrapper:nnn #1 #2 #3 { % keep changes to settings local by working inside a group \group_begin: % keep changes to settings local by working inside a group \keys_set:nn {aTableau} {#1} \__aTableau_tikzpicture:nn {#2} {#3} \group_end: } % usage: \__aTableau_beamer_wrapper:nnnn {overlay specification} {settings} {origin} {aTableau command} \cs_new_protected:Npn \__aTableau_beamer_wrapper:nnnn #1 #2 #3 #4 { % Keep changes to settings local by working inside a group % Process the keys before invoking \__aTableau_tikzpicture:nn so % that the beamer key can change the beamer overlay command \group_begin: \keys_set:nn {aTableau} {#2} \IfNoValueTF {#1} { \__aTableau_tikzpicture:nn {#3} {#4} } { % apply beamer overlay command \cs:w \l__aTableau_beamer_tl \cs_end: <#1> { \__aTableau_tikzpicture:nn {#3} {#4} } } \group_end: } % --------------------------------------------------------------------------- % Public facing aTableau commands % usage: \NewATableauCommand \Commmand {aTableau command name} {keys} % Define custom aTableau commands. We could shorten the definition of this % command by having only one \IfClassLoadedTF at the top, but it is better to % keep the normal and beamer versions of each command close together for easier % comparison. \NewDocumentCommand\NewATableauCommand{ mmm } { \str_case:nnF {#2} { {Abacus} { \IfClassLoadedTF {beamer} { % \AbacusCommand (x,y) [style] {#runners} {partition} \NewDocumentCommand#1{ d<> d() O{} m m } { \__aTableau_beamer_wrapper:nnnn {##1} {#3,##3} {##2} { \__aTableau_abacus:nn {##4} {##5} } } } { % \AbacusCommand (x,y) [style] {#runners} {partition} \NewDocumentCommand#1{ d() O{} m m } { \__aTableau_command_wrapper:nnn {#3,##2} {##1} { \__aTableau_abacus:nn {##3} {##4} } } } } {Diagram} { \IfClassLoadedTF {beamer} { % \DiagramCommand (x,y) [style] {entries} \NewDocumentCommand#1{ d<> d() O{} m } { \__aTableau_beamer_wrapper:nnnn {##1} {#3,##3} {##2} { \__aTableau_draw_diagram:n {##4} } } } { % \DiagramCommand (x,y) [style] {entries} \NewDocumentCommand#1{ d() O{} m } { \__aTableau_command_wrapper:nnn {#3,##2} {##1} { \__aTableau_draw_diagram:n {##3} } } } } {Multidiagram} { \IfClassLoadedTF {beamer} { % \MultidiagramCommand (x,y) [style] {entries} \NewDocumentCommand#1{ d<> d() O{} m } { \__aTableau_beamer_wrapper:nnnn {##1} {#3,##3} {##2} { \__aTableau_multidiagram:n {##4} } } } { % \MultidiagramCommand (x,y) [style] {entries} \NewDocumentCommand#1{ d() O{} m } { \__aTableau_command_wrapper:nnn {#3,##2} {##1} { \__aTableau_multidiagram:n {##3} } } } } {Multitableau} { \IfClassLoadedTF {beamer} { % \MultitableauCommand (x,y) [style] {entries} \NewDocumentCommand#1{ d<> d() O{} m } { \__aTableau_beamer_wrapper:nnnn {##1} {#3,##3} {##2} { \__aTableau_multitableau:n {##4} } } } { % \MultitableauCommand (x,y) [style] {entries} \NewDocumentCommand#1{ d() O{} m } { \__aTableau_command_wrapper:nnn {#3,##2} {##1} { \__aTableau_multitableau:n {##3} } } } } {RibbonTableau} { \IfClassLoadedTF {beamer} { % \RibbonTableauCommand (x,y) [style] {entries} \NewDocumentCommand#1{ d<> d() O{} m } { \__aTableau_beamer_wrapper:nnnn {##1} {#3,##3} {##2} { \__aTableau_ribbon_tableau:n {##4} } } } { % \RibbonTableauCommand (x,y) [style] {entries} \NewDocumentCommand#1{ d() O{} m } { \__aTableau_command_wrapper:nnn {#3,##2} {##1} { \__aTableau_ribbon_tableau:n {##3} } } } } {ShiftedDiagram} { \IfClassLoadedTF {beamer} { % \ShiftedDiagramCommand (x,y) [style] {entries} \NewDocumentCommand#1{ d<> d() O{} m } { \__aTableau_beamer_wrapper:nnnn {##1} {shifted,#3,##3} {##2} { \__aTableau_draw_diagram:n {##4} } } } { % \ShiftedDiagramCommand (x,y) [style] {entries} \NewDocumentCommand#1{ d() O{} m } { \__aTableau_command_wrapper:nnn {shifted,#3,##2} {##1} { \__aTableau_draw_diagram:n {##3} } } } } {ShiftedTableau} { \IfClassLoadedTF {beamer} { % \ShiftedTableauCommand (x,y) [style] {entries} \NewDocumentCommand#1{ d<> d() O{} m } { \__aTableau_beamer_wrapper:nnnn {##1} {shifted,#3,##3} {##2} { \__aTableau_draw_tableau:n {##4} } } } { % \ShiftedTableauCommand (x,y) [style] {entries} \NewDocumentCommand#1{ d() O{} m } { \__aTableau_command_wrapper:nnn {shifted,#3,##2} {##1} { \__aTableau_draw_tableau:n {##3} } } } } {SkewDiagram} { \IfClassLoadedTF {beamer} { % \SkewDiagramCommand (x,y) [style] {skew shape} {entries} \NewDocumentCommand#1{ d<> d() O{} m m } { \__aTableau_beamer_wrapper:nnnn {##1} {skew={##4},#3,##3} {##2} { \__aTableau_draw_diagram:n {##5} } } } { % \SkewDiagramCommand (x,y) [style] {skew shape} {entries} \NewDocumentCommand#1{ d() O{} m m } { \__aTableau_command_wrapper:nnn {skew={##3},#3,##2} {##1} { \__aTableau_draw_diagram:n {##4} } } } } {SkewTableau} { \IfClassLoadedTF {beamer} { % \SkewTableauCommand (x,y) [style] {skew shape} {entries} \NewDocumentCommand#1{ d<> d() O{} m m } { \__aTableau_beamer_wrapper:nnnn {##1} {skew={##4},#3,##3} {##2} { \__aTableau_draw_tableau:n {##5} } } } { % \SkewTableauCommand (x,y) [style] {skew shape} {entries} \NewDocumentCommand#1{ d() O{} m m } { \__aTableau_command_wrapper:nnn {skew={##3},#3,##2} {##1} { \__aTableau_draw_tableau:n {##4} } } } } {Tableau} { \IfClassLoadedTF {beamer} { % \TableauCommandCommand (x,y) [style] {entries} \NewDocumentCommand#1{ d<> d() O{} m } { \__aTableau_beamer_wrapper:nnnn {##1} {#3,##3} {##2} { \__aTableau_draw_tableau:n {##4} } } } { % \TableauCommandCommand (x,y) [style] {entries} \NewDocumentCommand#1{ d() O{} m } { \__aTableau_command_wrapper:nnn {#3,##2} {##1} { \__aTableau_draw_tableau:n {##3} } } } } {Tabloid} { \IfClassLoadedTF {beamer} { % \TabloidCommand (x,y) [style] {entries} \NewDocumentCommand#1{ d<> d() O{} m } { \__aTableau_beamer_wrapper:nnnn {##1} {tabloid,#3,##3} {##2} { \__aTableau_draw_tableau:n {##4} } } } { % \TabloidCommand (x,y) [style] {entries} \NewDocumentCommand#1{ d() O{} m } { \__aTableau_command_wrapper:nnn {tabloid,#3,##2} {##1} { \__aTableau_draw_tableau:n {##3} } } } } } { \msg_error:nnx {aTableau} {unrecognised-aTableau-command} {#2} } } % Use \NewATableauCommand command to define the aTableau commands \NewATableauCommand\Abacus{Abacus}{} \NewATableauCommand\Diagram{Diagram}{} \NewATableauCommand\Multidiagram{Multidiagram}{} \NewATableauCommand\Multitableau{Multitableau}{} \NewATableauCommand\RibbonTableau{RibbonTableau}{} \NewATableauCommand\ShiftedDiagram{ShiftedDiagram}{} \NewATableauCommand\ShiftedTableau{ShiftedTableau}{} \NewATableauCommand\SkewDiagram{SkewDiagram}{} \NewATableauCommand\SkewTableau{SkewTableau}{} \NewATableauCommand\Tableau{Tableau}{} \NewATableauCommand\Tabloid{Tabloid}{} % outfacing command to access aTableau keys/options \NewDocumentCommand\aTabset{ m }{ \keys_set:nn { aTableau } {#1} } % --------------------------------------------------------------------------- % Finally, now that everything is defined, process the package options. \ProcessKeyOptions[ aTableau ] \endinput % --------------------------------------------------------------------------- % % Copyright (C) 2022-25 by Andrew Mathas % % 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 % atableau_beamer.pdf % atableau_beamer.tex % atableau_readme.webp % LICENSE % README.md % CHANGES.md % TODO.md % % --------------------------------------------------------------------------- % end of atableau.sty