From 950eb9444a8f9d027a4ad7d10e4903eabe800981 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Sun, 8 Dec 2013 00:19:02 +0000 Subject: lt3graph (7dec13) git-svn-id: svn://tug.org/texlive/trunk@32356 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/tex/latex/lt3graph/lt3graph.sty | 1706 +++++++++++++++++++++ 1 file changed, 1706 insertions(+) create mode 100644 Master/texmf-dist/tex/latex/lt3graph/lt3graph.sty (limited to 'Master/texmf-dist/tex/latex/lt3graph/lt3graph.sty') diff --git a/Master/texmf-dist/tex/latex/lt3graph/lt3graph.sty b/Master/texmf-dist/tex/latex/lt3graph/lt3graph.sty new file mode 100644 index 00000000000..4c9b4fb6055 --- /dev/null +++ b/Master/texmf-dist/tex/latex/lt3graph/lt3graph.sty @@ -0,0 +1,1706 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \iffalse %%%% +% % +% Copyright (c) 2013 - Michiel Helvensteijn (www.mhelvens.net) % +% % +% https://github.com/mhelvens/latex-lt3graph % +% % +% This work may be distributed and/or modified under the conditions % +% of the LaTeX Project Public License, either version 1.3 of this % +% license or (at your option) any later version. The latest version % +% of this license is in http://www.latex-project.org/lppl.txt % +% and version 1.3 or later is part of all distributions of LaTeX % +% version 2005/12/01 or later. % +% % +% This work has the LPPL maintenance status 'maintained'. % +% % +% The Current Maintainer of this work is Michiel Helvensteijn. % +% % +% This work consists of the files lt3graph.tex and lt3graph.sty. % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \fi %%%% + +% \CheckSum{841} +% +% \CharacterTable +% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z +% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z +% Digits \0\1\2\3\4\5\6\7\8\9 +% Exclamation \! Double quote \" Hash (number) \# +% Dollar \$ Percent \% Ampersand \& +% Acute accent \' Left paren \( Right paren \) +% Asterisk \* Plus \+ Comma \, +% Minus \- Point \. Solidus \/ +% Colon \: Semicolon \; Less than \< +% Equals \= Greater than \> Question mark \? +% Commercial at \@ Left bracket \[ Backslash \\ +% Right bracket \] Circumflex \^ Underscore \_ +% Grave accent \` Left brace \{ Vertical bar \| +% Right brace \} Tilde \~} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% \subsection{Package Info} % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% \begin{macrocode} +\NeedsTeXFormat{LaTeX2e} +\RequirePackage{expl3} +\ProvidesExplPackage{lt3graph}{2013/12/05}{0.0.9-r1} + {a LaTeX3 datastructure for representing directed graphs with data} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% \subsection{Required Packages} % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% These are the packages we'll need: +% +% \begin{macrocode} +\RequirePackage{l3candidates} +\RequirePackage{l3clist} +\RequirePackage{l3keys2e} +\RequirePackage{l3msg} +\RequirePackage{l3prg} +\RequirePackage{l3prop} +\RequirePackage{xparse} +\RequirePackage{withargs} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% We now test whether the |display| option was used: +% +% \begin{macrocode} +\bool_new:N \__graph_format_bool +\bool_set_false:N \__graph_format_bool + +\keys_define:nn {lt3graph} + { display .code:n = \bool_set_true:N \__graph_format_bool } + +\ProcessKeysPackageOptions {lt3graph} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% If the |display| option was used, load additional packages: +% +% \begin{macrocode} +\bool_if:NT \__graph_format_bool { + \RequirePackage[table]{xcolor} +} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% \subsection{Additions to \LaTeX3 Fundamentals} % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% These are three macros for working with `set literals' +% in an expandable context. They use internal macros from +% |l3prop|... Something I'm really not supposed to do. +% +% \begin{macrocode} +\prg_new_conditional:Npnn \__graph_set_if_in:nn #1#2 { p } + { + \__prop_if_in:nwwn {#2} #1 \s_obj_end + \__prop_pair:wn #2 \s__prop { } + \q_recursion_tail + \__prg_break_point: + } + +\cs_set_eq:NN \__graph_empty_set \s__prop + +\cs_new:Nn \__graph_set_cons:nn { + #1 \__prop_pair:wn #2 \s__prop {} +} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% \subsection{Data Access} % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% These functions generate the multi-part csnames +% under which all graph data is stored: +% +% \begin{macrocode} +\cs_new:Nn \__graph_tl:n { g__graph_data (#1) _tl } +\cs_new:Nn \__graph_tl:nn { g__graph_data (#1) (#2) _tl } +\cs_new:Nn \__graph_tl:nnn { g__graph_data (#1) (#2) (#3) _tl } +\cs_new:Nn \__graph_tl:nnnn { g__graph_data (#1) (#2) (#3) (#4) _tl } +\cs_new:Nn \__graph_tl:nnnnn { g__graph_data (#1) (#2) (#3) (#4) (#5) _tl } +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% The following functions generate multi-part keys to +% use in property maps: +% +% \begin{macrocode} +\cs_new:Nn \__graph_key:n { key (#1) } +\cs_new:Nn \__graph_key:nn { key (#1) (#2) } +\cs_new:Nn \__graph_key:nnn { key (#1) (#2) (#3) } +\cs_new:Nn \__graph_key:nnnn { key (#1) (#2) (#3) (#4) } +\cs_new:Nn \__graph_key:nnnnn { key (#1) (#2) (#3) (#4) (#5) } +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% A quick way to iterate through property maps holding +% graph data: +% +% \begin{macrocode} +\cs_new_protected:Nn \__graph_for_each_prop_datatype:n + { \seq_map_inline:Nn \g__graph_prop_data_types_seq {#1} } +\seq_new:N \g__graph_prop_data_types_seq +\seq_set_from_clist:Nn \g__graph_prop_data_types_seq + {vertices, edge-values, edge-froms, edge-tos, edge-triples, + indegree, outdegree} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% \subsection{Storing data through pointers} % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% The following function embodies a \LaTeX3 design pattern +% for representing non-null pointers. This allows data to +% be 'protected' behind a macro redirection. Any number of +% expandable operations can be applied to the pointer +% indiscriminately without altering the data, even when +% using |:x|, |:o| or |:f| expansion. Expansion using |:v| +% dereferences the pointer and returns the data exactly +% as it was passed through |#2|. Expansion using |:c| +% returns a control sequence through which the data can +% be modified. +% +% \begin{macrocode} +\cs_new_protected:Nn \__graph_ptr_new:Nn { + \withargs [\uniquecsname] { + \tl_set:Nn #1 {##1} + \tl_new:c {##1} + \tl_set:cn {##1} {#2} + } +} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% \subsection{Creating and initializing graphs} % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Globally create a new graph: +% +% \begin{macrocode} +\cs_new_protected:Nn \graph_new:N { + \graph_if_exist:NTF #1 { + % TODO: error + }{ + \tl_new:N #1 + \tl_set:Nf #1 { \tl_trim_spaces:f {\str_tail:n{#1}} } + \__graph_for_each_prop_datatype:n + { \prop_new:c {\__graph_tl:nnn{graph}{#1}{##1}} } + } +} +\cs_generate_variant:Nn \tl_trim_spaces:n {f} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Remove all data from a graph: +% +% \begin{macrocode} +\cs_new_protected:Nn \graph_clear:N + {\__graph_clear:Nn #1 { } } +\cs_new_protected:Nn \graph_gclear:N + {\__graph_clear:Nn #1 {g} } +\cs_new_protected:Nn \__graph_clear:Nn { + \__graph_for_each_prop_datatype:n + { \use:c{prop_#2clear:c} {\__graph_tl:nnn{graph}{#1}{##1}} } +} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Create a new graph if it doesn't already exist, +% then remove all data from it: +% +% \begin{macrocode} +\cs_new_protected:Nn \graph_clear_new:N + { \__graph_clear_new:Nn #1 { } } +\cs_new_protected:Nn \graph_gclear_new:N + { \__graph_clear_new:Nn #1 {g} } +\cs_new_protected:Nn \__graph_clear_new:Nn { + \graph_if_exists:NF #1 + { \graph_new:N #1 } + \use:c{graph_#2clear:N} #1 +} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Set all data in graph |#1| equal to that in graph |#2|: +% +% \begin{macrocode} +\cs_new_protected:Nn \graph_set_eq:NN + { \__graph_set_eq:NNn #1 #2 { } } +\cs_new_protected:Nn \graph_gset_eq:NN + { \__graph_set_eq:NNn #1 #2 {g} } +\cs_new_protected:Nn \__graph_set_eq:NNn { + \use:c{graph_#3clear:N} #1 + \__graph_for_each_prop_datatype:n + { + \use:c{prop_#3set_eq:cc} + {\__graph_tl:nnn{graph}{#1}{##1}} + {\__graph_tl:nnn{graph}{#2}{##1}} + } +} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% An expandable test of whether a graph exists. It does not +% actually test whether the command sequence contains a +% graph and is essentially the same as |\cs_if_exist:N(TF)|: +% +% \begin{macrocode} +\cs_set_eq:NN \graph_if_exist:Np \cs_if_exist:Np +\cs_set_eq:NN \graph_if_exist:NT \cs_if_exist:NT +\cs_set_eq:NN \graph_if_exist:NF \cs_if_exist:NF +\cs_set_eq:NN \graph_if_exist:NTF \cs_if_exist:NTF +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% \subsection{Manipulating graphs} % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Put a new vertex inside a graph: +% +% \begin{macrocode} +\cs_new_protected:Nn \graph_put_vertex:Nn + { \__graph_put_vertex:Nnnn #1 {#2} {} { } } +\cs_new_protected:Nn \graph_gput_vertex:Nn + { \__graph_put_vertex:Nnnn #1 {#2} {} {g} } +\cs_new_protected:Nn \graph_put_vertex:Nnn + { \__graph_put_vertex:Nnnn #1 {#2} {#3} { } } +\cs_new_protected:Nn \graph_gput_vertex:Nnn + { \__graph_put_vertex:Nnnn #1 {#2} {#3} {g} } +\cs_new_protected:Nn \__graph_put_vertex:Nnnn + { + %%% create pointer to value + % + \__graph_ptr_new:Nn \l__graph_vertex_data_tl {#3} + + %%% add the vertex + % + \use:c {prop_#4put:cnV} {\__graph_tl:nnn{graph}{#1}{vertices}} + {#2} \l__graph_vertex_data_tl + + \graph_get_vertex:NnNT #1 {#2} \l_tmpa_tl { + %%% initialize degree to 0 + % + \use:c{prop_#4put:cnn} {\__graph_tl:nnn{graph}{#1}{indegree}} {#2}{0} + \use:c{prop_#4put:cnn} {\__graph_tl:nnn{graph}{#1}{outdegree}} {#2}{0} + } + } +\tl_new:N \l__graph_vertex_data_tl +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Put a new edge inside a graph: +% +% \begin{macrocode} +\cs_new_protected:Nn \graph_put_edge:Nnn + { \__graph_put_edge:Nnnnn #1 {#2} {#3} {} { } } +\cs_new_protected:Nn \graph_gput_edge:Nnn + { \__graph_put_edge:Nnnnn #1 {#2} {#3} {} {g} } +\cs_new_protected:Nn \graph_put_edge:Nnnn + { \__graph_put_edge:Nnnnn #1 {#2} {#3} {#4} { } } +\cs_new_protected:Nn \graph_gput_edge:Nnnn + { \__graph_put_edge:Nnnnn #1 {#2} {#3} {#4} {g} } +\cs_new_protected:Nn \__graph_put_edge:Nnnnn + { + \graph_get_vertex:NnNTF #1 {#2} \l__graph_from_value_tl { + \graph_get_vertex:NnNTF #1 {#3} \l__graph_to_value_tl { + \graph_get_edge:NnnNF #1 {#2} {#3} \l_tmpa_tl { + %%% increment outgoing degree of vertex #2 + % + \use:c{prop_#5put:cnf} {\__graph_tl:nnn{graph}{#1}{outdegree}} {#2} + {\int_eval:n { + \prop_get:cn {\__graph_tl:nnn{graph}{#1}{outdegree}} {#2} + 1 + }} + + %%% increment incoming degree of vertex #3 + % + \use:c{prop_#5put:cnf} {\__graph_tl:nnn{graph}{#1}{indegree}} {#3} + {\int_eval:n { + \prop_get:cn {\__graph_tl:nnn{graph}{#1}{indegree}} {#3} + 1 + }} + } + + %%% actually add the edge + % + \withargs:VVn \l__graph_from_value_tl \l__graph_to_value_tl { + \use:c{prop_#5put:cox} + { \__graph_tl:nnn{graph}{#1}{edge-froms} } + { \__graph_key:nn{#2}{#3} } + { \tl_to_str:n{#2} } + \use:c{prop_#5put:cox} + { \__graph_tl:nnn{graph}{#1}{edge-tos} } + { \__graph_key:nn{#2}{#3} } + { \tl_to_str:n{#3} } + \__graph_ptr_new:Nn \l__graph_edge_data_tl {#4} + \use:c{prop_#5put:coV} + { \__graph_tl:nnn{graph}{#1}{edge-values} } + { \__graph_key:nn{#2}{#3} } + \l__graph_edge_data_tl + \use:c{prop_#5put:cox} + { \__graph_tl:nnn{graph}{#1}{edge-triples} } + { \__graph_key:nn{#2}{#3} } + { {\tl_to_str:n{#2}} + {\tl_to_str:n{#3}} + {\l__graph_edge_data_tl} } + } + }{ + % TODO: Error ('to' vertex doesn't exist) + } + }{ + % TODO: Error ('from' vertex doesn't exist) + } + } +\cs_generate_variant:Nn \prop_gput:Nnn {cox, coV, cnf} +\cs_generate_variant:Nn \prop_put:Nnn {cox, coV, cnf} +\cs_generate_variant:Nn \withargs:nnn {VVn} +\tl_new:N \l__graph_edge_data_tl +\tl_new:N \l__graph_from_value_tl +\tl_new:N \l__graph_to_value_tl +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Remove a vertex from a graph, automatically removing +% any connected edges: +% +% \begin{macrocode} +\cs_new_protected:Nn \graph_remove_vertex:Nn + { \__graph_remove_vertex:Nnn #1 {#2} { } } +\cs_new_protected:Nn \graph_gremove_vertex:Nn + { \__graph_remove_vertex:Nnn #1 {#2} {g} } +\cs_new_protected:Nn \__graph_remove_vertex:Nnn + { + \graph_get_vertex:NnNT #1 {#2} \l__graph_vertex_data_tl { + %%% remove outgoing edges + % + \graph_map_outgoing_edges_inline:Nnn #1 {#2} + { \use:c{graph_#3remove_edge:Nnn} #1 {##1} {##2} } + + %%% remove incoming edges + % + \graph_map_incoming_edges_inline:Nnn #1 {#2} + { \use:c{graph_#3remove_edge:Nnn} #1 {##1} {##2} } + + %%% remove the vertex + % + \use{prop_#3remove:cn} {\__graph_tl:nnn{graph}{#1}{vertices}} {#2} + \use{prop_#3remove:cn} {\__graph_tl:nnn{graph}{#1}{indegree}} {#2} + \use{prop_#3remove:cn} {\__graph_tl:nnn{graph}{#1}{outdegree}} {#2} + } + } +\cs_generate_variant:Nn \prop_put:Nnn {cnV} +% \tl_new:N \l__graph_vertex_data_tl % reusing from other function +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Remove an edge from the graph: +% +% \begin{macrocode} +\cs_new_protected:Nn \graph_remove_edge:Nnn + { \__graph_remove_edge:Nnnn #1 {#2} {#3} { } } +\cs_new_protected:Nn \graph_gremove_edge:Nnn + { \__graph_remove_edge:Nnnn #1 {#2} {#3} {g} } +\cs_new_protected:Nn \__graph_remove_edge:Nnnn { + \graph_get_edge:NnnNT #1 {#2} {#3} \l__graph_edge_data_tl { + %%% decrement outdegree of vertex #2 + % + \use:c{prop_#4put:cnf} {\__graph_tl:nnn{graph}{#1}{outdegree}} {#2} + {\int_eval:n { + \prop_get:cn {\__graph_tl:nnn{graph}{#1}{outdegree}} {#2} - 1 + }} + + %%% decrement indegree of vertex #3 + % + \use:c{prop_#4put:cnf} {\__graph_tl:nnn{graph}{#1}{indegree}} {#3} + {\int_eval:n { + \prop_get:cn {\__graph_tl:nnn{graph}{#1}{indegree}} {#3} - 1 + }} + + %%% actually remove edge + % + \use:c{prop_#4remove:co} + { \__graph_tl:nnn{graph}{#1}{edge-froms} } + { \__graph_key:nn{#2}{#3} } + \use:c{prop_#4remove:co} + { \__graph_tl:nnn{graph}{#1}{edge-tos} } + { \__graph_key:nn{#2}{#3} } + \use:c{prop_#4remove:co} + { \__graph_tl:nnn{graph}{#1}{edge-values} } + { \__graph_key:nn{#2}{#3} } + \use:c{prop_#4remove:co} + { \__graph_tl:nnn{graph}{#1}{edge-triples} } + { \__graph_key:nn{#2}{#3} } + } +} +\cs_generate_variant:Nn \prop_remove:Nn {co} +\cs_generate_variant:Nn \prop_gremove:Nn {co} +\cs_generate_variant:Nn \prop_put:Nnn {cnf} +\cs_generate_variant:Nn \prop_gput:Nnn {cnf} +%\tl_new:N \l__graph_edge_data_tl % reusing from other function +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Add all edges from graph |#2| to graph |#1|, but only +% between nodes already present in |#1|: +% +% \begin{macrocode} +\cs_new_protected:Nn \graph_put_edges_from:NN + { \__graph_gput_edges_from:NNn #1 #2 { } } +\cs_new_protected:Nn \graph_gput_edges_from:NN + { \__graph_gput_edges_from:NNn #1 #2 {g} } +\cs_new_protected:Nn \__graph_gput_edges_from:NNn + { + \graph_map_edges_inline:Nn #2 { + \graph_if_vertex_exist:NnT #1 {##1} { + \graph_if_vertex_exist:NnT #1 {##2} { + \graph_gput_edge:Nnnn #1 {##1} {##2} {##3} + } + } + } + } +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% \subsection{Recovering values from graphs with branching} % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Test whether a vertex |#2| exists. If so, its value is +% stored in |#3| and |T| is left in the input stream. If it +% doesn't, |F| is left in the input stream. +% +% \begin{macrocode} +\prg_new_protected_conditional:Nnn \graph_get_vertex:NnN + {T, F, TF} + { + \prop_get:cnNTF { \__graph_tl:nnn {graph} {#1} {vertices} } {#2} #3 + { \tl_set:Nv #3 {#3} \prg_return_true: } + { \prg_return_false: } + } +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Test whether an edge |#2|--|#3| exists. If so, its value is +% stored in |#4| and |T| is left in the input stream. If it +% doesn't, |F| is left in the input stream. +% +% \begin{macrocode} +\prg_new_protected_conditional:Nnn \graph_get_edge:NnnN + {T, F, TF} + { + \prop_get:coNTF + { \__graph_tl:nnn{graph}{#1}{edge-values} } + { \__graph_key:nn{#2}{#3} } + #4 + { \tl_set:Nv #4 {#4} \prg_return_true: } + { \prg_return_false: } + } +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% \subsection{Graph Conditionals} % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% An expandable test for the existence of a vertex: +% +% \begin{macrocode} +\prg_new_conditional:Nnn \graph_if_vertex_exist:Nn + {p, T, F, TF} + { + \prop_if_in:cnTF + { \__graph_tl:nnn {graph} {#1} {vertices} } + { #2 } + { \prg_return_true: } + { \prg_return_false: } + } +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% An expandable test for the existence of an edge: +% +% \begin{macrocode} +\prg_new_conditional:Nnn \graph_if_edge_exist:Nnn + {p, T, F, TF} + { + \prop_if_in:coTF + { \__graph_tl:nnn {graph} {#1} {edge-values} } + { \__graph_key:nn{#2}{#3} } + { \prg_return_true: } + { \prg_return_false: } + } +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Test whether graph |#1| contains a cycle reachable from +% vertex |#2|: +% +% \begin{macrocode} +\cs_new:Npn \graph_if_vertex_can_reach_cycle_p:Nn #1#2 + { \__graph_if_vertex_can_reach_cycle_p:Nnn #1 {#2} {\__graph_empty_set} } +\cs_new:Npn \graph_if_vertex_can_reach_cycle:NnTF #1#2 + { \__graph_if_vertex_can_reach_cycle:NnnTF #1 {#2} {\__graph_empty_set} } +\cs_new:Npn \graph_if_vertex_can_reach_cycle:NnT #1#2 + { \__graph_if_vertex_can_reach_cycle:NnnT #1 {#2} {\__graph_empty_set} } +\cs_new:Npn \graph_if_vertex_can_reach_cycle:NnF #1#2 + { \__graph_if_vertex_can_reach_cycle:NnnF #1 {#2} {\__graph_empty_set} } + +\prg_new_conditional:Nnn \__graph_if_vertex_can_reach_cycle:Nnn + {p, T, F, TF} + % #1: graph id + % #2: vertex id + % #3: visited vertices in 'prop literal' format (internal l3prop) + { + \graph_map_outgoing_edges_tokens:Nnn #1 {#2} + { \__graph_if_vertex_can_reach_cycle:Nnnnn #1 {#3} } + \prg_return_false: + } + +\cs_new:Nn \__graph_if_vertex_can_reach_cycle:Nnnnn + % #1: graph id + % #2: visited vertices in 'prop literal' format (internal l3prop) + % #3: start vertex (not used) + % #4: current vertex + % #5: edge value (behind ptr, not used) + { + \bool_if:nT + { + \__graph_set_if_in_p:nn {#2} {#4} || + \__graph_if_vertex_can_reach_cycle_p:Nno #1 {#4} + { \__graph_set_cons:nn {#2} {#4} } + } + { \prop_map_break:n {\use_i:nn \prg_return_true:} } + } +\cs_generate_variant:Nn \__graph_if_vertex_can_reach_cycle_p:Nnn {Nno} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Test whether graph |#1| contains any cycles: +% +% \begin{macrocode} +\prg_new_conditional:Nnn \graph_if_cyclic:N + {p, T, F, TF} + % #1: graph id + { + \graph_map_vertices_tokens:Nn #1 + { \__graph_if_cyclic:Nnn #1 } + \prg_return_false: + } + +\cs_new:Nn \__graph_if_cyclic:Nnn + % #1: graph id + % #2: vertex id + % #3: vertex value (not used) + { + \bool_if:nT + { \graph_if_vertex_can_reach_cycle_p:Nn #1 {#2} } + { \prop_map_break:n {\use_i:nn \prg_return_true:} } + } +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Assume that graph |#1| is acyclic and test +% whether a path exists from |#2| to |#3|: +% +% \begin{macrocode} +\prg_new_conditional:Nnn \graph_acyclic_if_path_exist:Nnn + {p, T, F, TF} + % #1: graph id + % #2: start vertex + % #3: end vertex + { + \graph_map_outgoing_edges_tokens:Nnn #1 {#2} + { \__graph_acyclic_if_path_exist:Nnnnn #1 {#3} } + \prg_return_false: + } + +\cs_new:Nn \__graph_acyclic_if_path_exist:Nnnnn + % #1: graph id + % #2: end vertex + % #3: start vertex (not used) + % #4: possible end vertex + % #5: edge value (behind ptr, do not use) + { + \bool_if:nT + { + \str_if_eq_p:nn {#4} {#2} || + \graph_acyclic_if_path_exist_p:Nnn #1 {#4} {#2} + } + { \prop_map_break:n {\use_i:nn \prg_return_true:} } + } +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% \subsection{Querying Information} % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Get the number of edges leading out of vertex |#2|: +% +% \begin{macrocode} +\cs_new:Nn \graph_get_outdegree:Nn { + \prop_get:cn {\__graph_tl:nnn{graph}{#1}{outdegree}} {#2} +} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Get the number of edges leading into vertex |#2|: +% +% \begin{macrocode} +\cs_new:Nn \graph_get_indegree:Nn { + \prop_get:cn {\__graph_tl:nnn{graph}{#1}{indegree}} {#2} +} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Get the number of edges connected to vertex |#2|: +% +% \begin{macrocode} +\cs_new:Nn \graph_get_degree:Nn { + \int_eval:n{ \graph_get_outdegree:Nn #1 {#2} + + \graph_get_indegree:Nn #1 {#2} } +} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% \subsection{Mapping Graphs} % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Applies the tokens |#2| to all vertex name/value pairs in +% the graph. The tokens are supplied with two arguments as +% trailing brace groups. +% +% \begin{macrocode} +\cs_new:Nn \graph_map_vertices_tokens:Nn { + \prop_map_tokens:cn + { \__graph_tl:nnn{graph}{#1}{vertices} } + { \__graph_map_vertices_tokens_aux:nnv {#2} } +} +\cs_new:Nn \__graph_map_vertices_tokens_aux:nnn + { #1 {#2} {#3} } +\cs_generate_variant:Nn \__graph_map_vertices_tokens_aux:nnn {nnv} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Applies the function |#2| to all vertex name/value pairs in +% the graph. The function is supplied with two arguments as +% trailing brace groups. +% +% \begin{macrocode} +\cs_new:Nn \graph_map_vertices_function:NN { + \prop_map_tokens:cn + { \__graph_tl:nnn{graph}{#1}{vertices} } + { \exp_args:Nnv #2 } +} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Applies the inline function |#2| to all vertex name/value +% pairs in the graph. The inline function is supplied with +% two arguments: `|#1|' for the name, `|#2|' for the value. +% +% \begin{macrocode} +\cs_new_protected:Nn \graph_map_vertices_inline:Nn { + \withargs (c) [\uniquecsname] [#2] { + \cs_set:Npn ##1 ####1####2 {##2} + \graph_map_vertices_function:NN #1 ##1 + } +} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Applies the tokens |#2| to all edge from/to/value triples +% in the graph. The tokens are supplied with three +% arguments as trailing brace groups. +% +% \begin{macrocode} +\cs_new:Nn \graph_map_edges_tokens:Nn { + \prop_map_tokens:cn + { \__graph_tl:nnn{graph}{#1}{edge-triples} } + { \__graph_map_edges_tokens_aux:nnn {#2} } +} +\cs_new:Nn \__graph_map_edges_tokens_aux:nnn + { \__graph_map_edges_tokens_aux:nnnv {#1} #3 } +\cs_new:Nn \__graph_map_edges_tokens_aux:nnnn + { #1 {#2} {#3} {#4} } +\cs_generate_variant:Nn \__graph_map_edges_tokens_aux:nnnn {nnnv} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Applies the function |#2| to all edge from/to/value triples +% in the graph. The function is supplied with three +% arguments as trailing brace groups. +% +% \begin{macrocode} +\cs_new:Nn \graph_map_edges_function:NN { + \prop_map_tokens:cn + { \__graph_tl:nnn{graph}{#1}{edge-triples} } + { \__graph_map_edges_function_aux:Nnn #2 } +} +\cs_new:Nn \__graph_map_edges_function_aux:Nnn + { \__graph_map_edges_function_aux:Nnnv #1 #3 } +\cs_new:Nn \__graph_map_edges_function_aux:Nnnn + { #1 {#2} {#3} {#4} } +\cs_generate_variant:Nn \__graph_map_edges_function_aux:Nnnn {Nnnv} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Applies the tokens |#2| to all edge from/to/value triples +% in the graph. The tokens are supplied with three +% arguments: `|#1|' for the `from' vertex, `|#2|' for the +% `to' vertex and `|#3|' for the edge value. +% +% \begin{macrocode} +\cs_new_protected:Nn \graph_map_edges_inline:Nn { + \withargs (c) [\uniquecsname] [#2] { + \cs_set:Npn ##1 ####1####2####3 {##2} + \graph_map_edges_function:NN #1 ##1 + } +} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Applies the tokens |#3| to the from/to/value triples +% for the edges going `to' vertex |#2|. The tokens are +% supplied with three arguments as trailing brace groups. +% +% \begin{macrocode} +\cs_new:Nn \graph_map_incoming_edges_tokens:Nnn { + % #1: graph + % #2: base vertex + % #3: tokens to execute + \prop_map_tokens:cn + { \__graph_tl:nnn{graph}{#1}{edge-triples} } + { \__graph_map_incoming_edges_tokens_aux:nnnn {#2} {#3} } +} +\cs_new:Nn \__graph_map_incoming_edges_tokens_aux:nnnn + % #1: base vertex + % #2: tokens to execute + % #3: edge key + % #4: edge-triple {from}{to}{value} + { \__graph_map_incoming_edges_tokens_aux:nnnnv {#1} {#2} #4 } +\cs_new:Nn \__graph_map_incoming_edges_tokens_aux:nnnnn + % #1: base vertex + % #2: tokens to execute + % #3: edge 'from' vertex + % #4: edge 'to' vertex + % #5: edge value + { \str_if_eq:nnT {#1} {#4} { #2 {#3} {#4} {#5} } } +\cs_generate_variant:Nn \__graph_map_incoming_edges_tokens_aux:nnnnn {nnnnv} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Applies the function |#3| to the from/to/value triples +% for the edges going `to' vertex |#2|. The function is +% supplied with three arguments as trailing brace groups. +% +% \begin{macrocode} +\cs_new:Nn \graph_map_incoming_edges_function:NnN { + % #1: graph + % #2: base vertex + % #3: function to execute + \prop_map_tokens:cn + { \__graph_tl:nnn{graph}{#1}{edge-triples} } + { \__graph_map_incoming_edges_function_aux:nNnn {#2} #3 } +} +\cs_new:Nn \__graph_map_incoming_edges_function_aux:nNnn + % #1: base vertex + % #2: function to execute + % #3: edge key + % #4: edge-triple {from}{to}{value} + { \__graph_map_incoming_edges_function_aux:nNnnv {#1} #2 #4 } +\cs_new:Nn \__graph_map_incoming_edges_function_aux:nNnnn + % #1: base vertex + % #2: function to execute + % #3: edge 'from' vertex + % #4: edge 'to' vertex + % #5: edge value + { \str_if_eq:nnT {#1} {#4} { #2 {#3} {#4} {#5} } } +\cs_generate_variant:Nn \__graph_map_incoming_edges_function_aux:nNnnn {nNnnv} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Applies the inline function |#3| to the from/to/value triples +% for the edges going `to' vertex |#2|. The inline function is +% supplied with three arguments: `|#1|' for the `from' vertex, +% `|#2|' is equal to the |#2| supplied to this function and +% `|#3|' contains the edge value. +% +% \begin{macrocode} +\cs_new_protected:Nn \graph_map_incoming_edges_inline:Nnn { + % #1: graph + % #2: base vertex + % #3: body to execute + \withargs (c) [\uniquecsname] [#2] [#3] { + \cs_set:Npn ##1 ####1####2####3 {##3} + \graph_map_incoming_edges_function:NnN #1 {##2} ##1 + } +} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Applies the tokens |#3| to the from/to/value triples +% for the edges going `from' vertex |#2|. The tokens are +% supplied with three arguments as trailing brace groups. +% +% \begin{macrocode} +\cs_new:Nn \graph_map_outgoing_edges_tokens:Nnn { + % #1: graph + % #2: base vertex + % #3: tokens to execute + \prop_map_tokens:cn + { \__graph_tl:nnn{graph}{#1}{edge-triples} } + { \__graph_map_outgoing_edges_tokens_aux:nnnn {#2} {#3} } +} +\cs_new:Nn \__graph_map_outgoing_edges_tokens_aux:nnnn + % #1: base vertex + % #2: tokens to execute + % #3: edge key (not used) + % #4: edge-triple {from}{to}{value} + { \__graph_map_outgoing_edges_tokens_aux:nnnnv {#1} {#2} #4 } +\cs_new:Nn \__graph_map_outgoing_edges_tokens_aux:nnnnn + % #1: base vertex + % #2: tokens to execute + % #3: edge 'from' vertex + % #4: edge 'to' vertex + % #5: edge value + { \str_if_eq:nnT {#1} {#3} { #2 {#3} {#4} {#5} } } +\cs_generate_variant:Nn \__graph_map_outgoing_edges_tokens_aux:nnnnn {nnnnv} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Applies the function |#3| to the from/to/value triples +% for the edges going `from' vertex |#2|. The function is +% supplied with three arguments as trailing brace groups. +% +% \begin{macrocode} +\cs_new:Nn \graph_map_outgoing_edges_function:NnN { + % #1: graph + % #2: base vertex + % #3: function to execute + \prop_map_tokens:cn + { \__graph_tl:nnn{graph}{#1}{edge-triples} } + { \__graph_map_outgoing_edges_function_aux:nNnn {#2} #3 } +} +\cs_new:Nn \__graph_map_outgoing_edges_function_aux:nNnn + % #1: base vertex + % #2: function to execute + % #3: edge key + % #4: edge-triple {from}{to}{value} + { \__graph_map_outgoing_edges_function_aux:nNnnv {#1} #2 #4 } +\cs_new:Nn \__graph_map_outgoing_edges_function_aux:nNnnn + % #1: base vertex + % #2: function to execute + % #3: edge 'from' vertex + % #4: edge 'to' vertex + % #5: edge value + { \str_if_eq:nnT {#1} {#3} { #2 {#3} {#4} {#5} } } +\cs_generate_variant:Nn \__graph_map_outgoing_edges_function_aux:nNnnn {nNnnv} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Applies the inline function |#3| to the from/to/value triples +% for the edges going `from' vertex |#2|. The inline function is +% supplied with three arguments: `|#1|' is equal to the |#2| +% supplied to this function, `|#2|' contains the `to' vertex and +% `|#3|' contains the edge value. +% +% \begin{macrocode} +\cs_new_protected:Nn \graph_map_outgoing_edges_inline:Nnn { + % #1: graph + % #2: base vertex + % #3: body to execute + \withargs (c) [\uniquecsname] [#2] [#3] { + \cs_set:Npn ##1 ####1####2####3 {##3} + \graph_map_outgoing_edges_function:NnN #1 {##2} ##1 + } +} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + + + + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Applies the tokens |#3| to the key/value pairs +% of the vertices reachable from vertex |#2| in one step. +% The tokens are +% supplied with two arguments as trailing brace groups. +% +% \begin{macrocode} +\cs_new:Nn \graph_map_successors_tokens:Nnn { + % #1: graph + % #2: base vertex + % #3: tokens to execute + \prop_map_tokens:cn + { \__graph_tl:nnn{graph}{#1}{edge-triples} } + { \__graph_map_successors_tokens_aux:Nnnnn #1 {#2} {#3} } +} +\cs_new:Nn \__graph_map_successors_tokens_aux:Nnnnn { + % #1: the graph + % #2: base vertex + % #3: tokens to execute + % #4: edge key (not used) + % #5: edge-triple {from}{to}{value} + \__graph_map_successors_tokens_aux:Nnnnnn #1 {#2} {#3} #5 +} +\cs_new:Nn \__graph_map_successors_tokens_aux:Nnnnnn { + % #1: the graph + % #2: base vertex + % #3: tokens to execute + % #4: edge 'from' vertex + % #5: edge 'to' vertex + % #6: ptr to edge value (not used) + \str_if_eq:nnT {#2} {#4} { + \__graph_map_successors_tokens_aux:nnv + {#3} {#5} {\prop_get:cn{\__graph_tl:nnn{graph}{#1}{vertices}}{#5}} + } +} +\cs_new:Nn \__graph_map_successors_tokens_aux:nnn { + % #1: tokens to execute + % #2: successor key + % #3: successor value + #1 {#2} {#3} +} +\cs_generate_variant:Nn \__graph_map_successors_tokens_aux:nnn {nnv} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Applies the function |#3| to the key/value pairs +% of the vertices reachable from vertex |#2| in one step. +% The function is +% supplied with two arguments as trailing brace groups. +% +% \begin{macrocode} +\cs_new:Nn \graph_map_successors_function:NnN { + % #1: graph + % #2: base vertex + % #3: function to execute + \prop_map_tokens:cn + { \__graph_tl:nnn{graph}{#1}{edge-triples} } + { \__graph_map_successors_function_aux:NnNnn #1 {#2} #3 } +} +\cs_new:Nn \__graph_map_successors_function_aux:NnNnn { + % #1: the graph + % #2: base vertex + % #3: function to execute + % #4: edge key (not used) + % #5: edge-triple {from}{to}{value} + \__graph_map_successors_function_aux:NnNnnn #1 {#2} #3 #5 +} +\cs_new:Nn \__graph_map_successors_function_aux:NnNnnn { + % #1: the graph + % #2: base vertex + % #3: function to execute + % #4: edge 'from' vertex + % #5: edge 'to' vertex + % #6: ptr to edge value (not used) + \str_if_eq:nnT {#2} {#4} { + \__graph_map_successors_function_aux:Nnv + #3 {#5} {\prop_get:cn{\__graph_tl:nnn{graph}{#1}{vertices}}{#5}} + } +} +\cs_new:Nn \__graph_map_successors_function_aux:Nnn { + % #1: function to execute + % #2: successor key + % #3: successor value + #1 {#2} {#3} +} +\cs_generate_variant:Nn \__graph_map_successors_function_aux:Nnn {Nnv} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Applies the inline function |#3| to the key/value pairs +% of the vertices reachable from vertex |#2| in one step. +% The inline function is +% supplied with two arguments: `|#1|' is the key, and `|#2|' +% is the value of the successor vertex. +% +% \begin{macrocode} +\cs_new_protected:Nn \graph_map_successors_inline:Nnn { + % #1: graph + % #2: base vertex + % #3: body to execute + \withargs (c) [\uniquecsname] [#2] [#3] { + \cs_set:Npn ##1 ####1####2####3 {##3} + \graph_map_successors_function:NnN #1 {##2} ##1 + } +} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + + + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Applies the tokens |#2| to all vertex name/value pairs in +% topological order. The tokens are supplied with two +% arguments as trailing brace groups. +% Assumes that the graph is acyclic (for now). +% +% \begin{macrocode} +\cs_new_protected:Nn \graph_map_topological_order_tokens:Nn { + %%% Fill \l__graph_source_vertices with source-nodes and count indegrees + % + \prop_clear:N \l__graph_source_vertices + \graph_map_vertices_inline:Nn #1 { + \prop_put:Nnf \l__graph_tmp_indegrees_int {##1} + {\graph_get_indegree:Nn #1 {##1}} + \int_compare:nT {\graph_get_indegree:Nn #1 {##1} = 0} + { \prop_put:Nnn \l__graph_source_vertices {##1} {} } + } + + %%% Main loop + % + \bool_until_do:nn {\prop_if_empty_p:N \l__graph_source_vertices} { + %%% Choose any vertex (\l__graph_topo_key_tl, \l__graph_topo_value_tl) + % + \__graph_prop_any_key_pop:NN \l__graph_source_vertices \l__graph_topo_key_tl + \graph_get_vertex:NVNT #1 \l__graph_topo_key_tl \l__graph_topo_val_tl { + %%% Run the mapping funtion on the key and value from that vertex + % + \withargs:VVn \l__graph_topo_key_tl \l__graph_topo_val_tl + { #2 {##1} {##2} } + + %%% Deduct one from the counter of all affected nodes + %%% and add all now-empty vertices to \l__graph_source_vertices + % + \graph_map_outgoing_edges_inline:NVn #1 \l__graph_topo_key_tl { + \prop_put:Nnf \l__graph_tmp_indegrees_int {##2} + {\int_eval:n {\prop_get:Nn \l__graph_tmp_indegrees_int {##2} - 1}} + \int_compare:nT {\prop_get:Nn \l__graph_tmp_indegrees_int {##2} = 0} { + \prop_put:Nnn \l__graph_source_vertices {##2} {} + } + } + } + } +} +\cs_new_protected:Nn \__graph_prop_any_key_pop:NN { + \prop_map_inline:Nn #1 { + \tl_set:Nn #2 {##1} + \prop_remove:Nn #1 {##1} + \prop_map_break:n {\use_none:n} + } + { \tl_set:Nn #2 {\q_no_value} } +} +\cs_generate_variant:Nn \withargs:nnn {VVn} +\cs_generate_variant:Nn \graph_map_outgoing_edges_inline:Nnn {NVn} +\cs_generate_variant:Nn \prop_put:Nnn {Nnf} +\cs_generate_variant:Nn \graph_get_vertex:NnNT {NVNT} +\prop_new:N \l__graph_source_vertices +\prop_new:N \l__graph_tmp_indegrees_int +\tl_new:N \l__graph_topo_key_tl +\tl_new:N \l__graph_topo_val_tl +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Applies the function |#2| to all vertex name/value pairs in +% topological order. The function is supplied with two +% arguments as trailing brace groups. +% Assumes that the graph is acyclic (for now). +% +% \begin{macrocode} +\cs_new:Nn \graph_map_topological_order_function:NN + { \graph_map_topological_order_tokens:Nn #1 {#2} } +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Applies the inline function |#2| to all vertex name/value +% pairs in topological order. The inline function is supplied +% with two arguments: `|#1|' for the name and `|#2|' for the value. +% Assumes that the graph is acyclic (for now). +% +% \begin{macrocode} +\cs_new_protected:Nn \graph_map_topological_order_inline:Nn { + \withargs (c) [\uniquecsname] [#2] { + \cs_set:Npn ##1 ####1####2 {##2} + \graph_map_topological_order_function:NN #1 ##1 + } +} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% \subsection{Transforming Graphs} % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Set graph |#1| to the transitive closure of graph |#2|. +% +% \begin{macrocode} +\cs_new_protected:Nn \graph_set_transitive_closure:NN + { \__graph_set_transitive_closure:NNNnn #1 #2 \use_none:nnn {} { } } +\cs_new_protected:Nn \graph_gset_transitive_closure:NN + { \__graph_set_transitive_closure:NNNnn #1 #2 \use_none:nnn {} {g} } +\cs_new_protected:Nn \graph_set_transitive_closure:NNNn + { \__graph_set_transitive_closure:NNNnn #1 #2 #3 {#4} { } } +\cs_new_protected:Nn \graph_gset_transitive_closure:NNNn + { \__graph_set_transitive_closure:NNNnn #1 #2 #3 {#4} {g} } +\cs_new_protected:Nn \__graph_set_transitive_closure:NNNnn + % #1: target + % #2: source + % #3: combination function with argspec :nnn + % #4: default 'old' value + { + \use:c{graph_#5set_eq:NN} #1 #2 + + \cs_set:Nn \__graph_edge_combinator:nnn + { \exp_not:n { #3 {##1} {##2} {##3} } } + \cs_generate_variant:Nn \__graph_edge_combinator:nnn {VVV} + + \graph_map_vertices_inline:Nn #2 { + \graph_map_vertices_inline:Nn #2 { + \graph_get_edge:NnnNT #2 {##1} {####1} \l__graph_edge_value_i_tl { + \graph_map_vertices_inline:Nn #2 { + \graph_get_edge:NnnNT #2 {####1} {########1} + \l__graph_edge_value_ii_tl { + \graph_get_edge:NnnNF #1 {##1} {########1} + \l__graph_edge_value_old_tl + { \tl_set:Nn \l__graph_edge_value_old_tl {#4} } + \exp_args:NNx \tl_set:No \l__graph_edge_value_new_tl { + \__graph_edge_combinator:VVV + \l__graph_edge_value_i_tl + \l__graph_edge_value_ii_tl + \l__graph_edge_value_old_tl + } + \use:c{graph_#5put_edge:NnnV} #1 {##1} {########1} + \l__graph_edge_value_new_tl + } + } + } + } + } + } +\cs_generate_variant:Nn \graph_put_edge:Nnnn {NnnV} +\cs_generate_variant:Nn \graph_gput_edge:Nnnn {NnnV} +\cs_generate_variant:Nn \tl_to_str:n {o} +\tl_new:N \l__graph_edge_value_i_tl +\tl_new:N \l__graph_edge_value_ii_tl +\tl_new:N \l__graph_edge_value_old_tl +\tl_new:N \l__graph_edge_value_new_tl +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% Assume that graph |#2| contains no cycles, and set graph |#1| to its transitive reduction. +% +% \begin{macrocode} +\cs_new_protected:Nn \graph_set_transitive_reduction:NN + { \__graph_set_transitive_reduction:NNn #1 #2 { } } +\cs_new_protected:Nn \graph_gset_transitive_reduction:NN + { \__graph_set_transitive_reduction:NNn #1 #2 {g} } +\cs_new_protected:Nn \__graph_set_transitive_reduction:NNn + % #1: target + % #2: source + { + \use:c{graph_#3set_eq:NN} #1 #2 + \graph_map_vertices_inline:Nn #2 { + \graph_map_vertices_inline:Nn #2 { + \graph_get_edge:NnnNT #2 {##1} {####1} \l_tmpa_tl { + \graph_map_vertices_inline:Nn #2 { + \graph_get_edge:NnnNT #2 {####1} {########1} \l_tmpa_tl { + \use:c{graph_#3remove_edge:Nnn} #1 {##1} {########1} + } + } + } + } + } + } +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% \subsection{Displaying Graphs} % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% If the |display| option was given, we define some additional +% functions that can display the graph in table-form. +% +% \begin{macrocode} +\bool_if:NT \__graph_format_bool { +% \end{macrocode} +% +% This is the option-less version, which delegates +% to the full version of the function: +% +% \begin{macrocode} +\cs_new_protected:Nn \graph_display_table:N + { \graph_display_table:Nn #1 {} } +% \end{macrocode} +% +% The full version has a second argument accepting options +% that determine table formatting. We first define those options: +% +% \begin{macrocode} +\keys_define:nn {lt3graph-display} { + row_keys .bool_set:N = \l__graph_display_row_keys_bool, + row_keys .initial:n = {true}, + row_keys .default:n = {true}, + + vertex_vals .bool_set:N = \l__graph_display_vertex_vals_bool, + vertex_vals .initial:n = {true}, + vertex_vals .default:n = {true}, + + row_keys_format .tl_set:N = \l__graph_format_row_keys_tl, + row_keys_format .initial:n = \textbf, + row_keys_format .value_required:, + + col_keys_format .tl_set:N = \l__graph_format_col_keys_tl, + col_keys_format .initial:n = \textbf, + col_keys_format .value_required:, + + vertex_vals_format .tl_set:N = \l__graph_format_vertex_vals_tl, + vertex_vals_format .initial:n = \use:n, + vertex_vals_format .value_required:, + + edge_vals_format .tl_set:N = \l__graph_format_edge_vals_tl, + edge_vals_format .initial:n = \use:n, + edge_vals_format .value_required:, + + edge_diagonal_format .tl_set:N = \l__graph_format_edge_diagonal_tl, + edge_diagonal_format .initial:n = \cellcolor{black!30!white}, + edge_diagonal_format .value_required:, + + edge_direct_format .tl_set:N = \l__graph_format_edge_direct_tl, + edge_direct_format .initial:n = \cellcolor{green}, + edge_direct_format .value_required:, + + edge_transitive_format .tl_set:N = \l__graph_format_edge_transitive_tl, + edge_transitive_format .initial:n = \cellcolor{green!40!yellow}\tiny(tr), + edge_transitive_format .value_required:, + + edge_none_format .tl_set:N = \l__graph_format_edge_none_tl, + edge_none_format .initial:n = {}, + edge_none_format .value_required: +} +% \end{macrocode} +% +% Now we define the function itself. +% It displays a table showing the structure and content +% of graph |#1|. If argument |#2| is passed, its options +% are applied to format the output. +% +% \begin{macrocode} +\cs_new_protected:Nn \graph_display_table:Nn { + \group_begin: +% \end{macrocode} +% +% We process those options passed with |#2|: +% +% \begin{macrocode} + \keys_set:nn {lt3graph-display} {#2} +% \end{macrocode} +% +% We populate the top row of the table: +% +% \begin{macrocode} + \tl_put_right:Nn \l__graph_table_content_tl {\hline} + \seq_clear:N \l__graph_row_seq + \bool_if:NT \l__graph_display_row_keys_bool + { \seq_put_right:Nn \l__graph_row_seq {} + \tl_put_right:Nn \l__graph_table_colspec_tl {|r|} } + \bool_if:NT \l__graph_display_vertex_vals_bool + { \seq_put_right:Nn \l__graph_row_seq {} + \tl_put_right:Nn \l__graph_table_colspec_tl {|c|} } + \graph_map_vertices_inline:Nn #1 { + \tl_put_right:Nn \l__graph_table_colspec_tl {|c} + \seq_put_right:Nn \l__graph_row_seq + { { \l__graph_format_col_keys_tl {##1} } } + } + \tl_put_right:Nn \l__graph_table_colspec_tl {|} + \tl_put_right:Nx \l__graph_table_content_tl + { \seq_use:Nn \l__graph_row_seq {&} } + \tl_put_right:Nn \l__graph_table_content_tl + { \\\hline\hline } +% \end{macrocode} +% +% We populate the remaining rows: +% +% \begin{macrocode} + \graph_map_vertices_inline:Nn #1 { + \seq_clear:N \l__graph_row_seq + \bool_if:NT \l__graph_display_row_keys_bool { + \seq_put_right:Nn \l__graph_row_seq + { { \l__graph_format_row_keys_tl {##1} } } } + \bool_if:NT \l__graph_display_vertex_vals_bool { + \seq_put_right:Nn \l__graph_row_seq + { { \l__graph_format_vertex_vals_tl {##2} } } } + \graph_map_vertices_inline:Nn #1 { +% \end{macrocode} +% +% We start building the vertex cell value. First we distinguish +% between a direct connection, a transitive connection, +% and no connection, and format accordingly: +% +% \begin{macrocode} + \graph_get_edge:NnnNTF #1 {##1} {####1} \l_tmpa_tl { + \quark_if_no_value:VF \l_tmpa_tl { + \tl_set_eq:NN \l__graph_cell_content_tl \l_tmpa_tl + \tl_set:Nf \l__graph_cell_content_tl + { \exp_args:NV \l__graph_format_edge_direct_tl + \l__graph_cell_content_tl } } + }{\graph_acyclic_if_path_exist:NnnTF #1 {##1} {####1} { + \tl_set_eq:NN \l__graph_cell_content_tl + \l__graph_format_edge_transitive_tl + }{ + \tl_set_eq:NN \l__graph_cell_content_tl + \l__graph_format_edge_none_tl + }} +% \end{macrocode} +% +% Secondary formatting comes from cells on the diagonal, +% i.e., a key compared to itself: +% +% \begin{macrocode} + \str_if_eq:nnT {##1} {####1} { + \tl_set:Nf \l__graph_cell_content_tl + { \exp_args:NV \l__graph_format_edge_diagonal_tl + \l__graph_cell_content_tl } } +% \end{macrocode} +% +% Tertiary formatting is applied to all vertex value cells: +% +% \begin{macrocode} + \tl_set:Nf \l__graph_cell_content_tl + { \exp_args:NV \l__graph_format_edge_vals_tl + \l__graph_cell_content_tl } +% \end{macrocode} +% +% We can now add the cell to the row sequence: +% +% \begin{macrocode} + \seq_put_right:NV \l__graph_row_seq \l__graph_cell_content_tl +% \end{macrocode} +% \uninteresting\begin{macrocode} + } +% \end{macrocode} +% +% We are finished with this row; go on to the next iteration: +% +% \begin{macrocode} + \tl_put_right:Nx \l__graph_table_content_tl { \seq_use:Nn \l__graph_row_seq {&} } + \tl_put_right:Nn \l__graph_table_content_tl {\\\hline} +% \end{macrocode} +% \uninteresting\begin{macrocode} + } +% \end{macrocode} +% +% Finally, we print the table itself: +% +% \begin{macrocode} + \withargs:VVn \l__graph_table_colspec_tl \l__graph_table_content_tl + { \begin{tabular}{##1}##2\end{tabular} } +% \end{macrocode} +% \uninteresting\begin{macrocode} + \group_end: +} +% \end{macrocode} +% +% Now follow the local variants and variables +% used in the function: +% +% \begin{macrocode} +\cs_generate_variant:Nn \quark_if_no_value:nF {VF} +\cs_generate_variant:Nn \withargs:nnn {VVn} +\tl_new:N \l__graph_table_colspec_tl +\tl_new:N \l__graph_table_content_tl +\tl_new:N \l__graph_cell_content_tl +\bool_new:N \l__graph_table_skipfirst_bool +\seq_new:N \l__graph_row_seq +% \end{macrocode} +% \uninteresting\begin{macrocode} +} +% \end{macrocode} +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + -- cgit v1.2.3