%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \iffalse %%%%
% %
% Copyright (c) 2017 - 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{0}
%
% \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}{2017/09/20}{0.1.9}
{a LaTeX3 datastructure for representing directed graphs with data}
% \end{macrocode}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% \subsection{Required Packages} %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% These are the packages we'll need:
%
% \begin{macrocode}
\RequirePackage{l3keys2e}
\RequirePackage{xparse}
\RequirePackage{withargs}
% \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}
}
}
\cs_new_protected:Nn \__graph_ptr_gnew:Nn {
\withargs [\uniquecsname] {
\tl_gset:Nn #1 {##1}
\tl_new:c {##1}
\tl_gset: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}} }
\int_new:c {\__graph_tl:nnn{graph}{#1}{vertex-count}}
\__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
%
\use:c{__graph_ptr_#4new: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
%%% increment the vertex counter
%
\use:c{int_#4incr:c} {\__graph_tl:nnn{graph}{#1}{vertex-count}}
\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_item: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_item: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} }
\use:c{__graph_ptr_#5new: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:c{prop_#3remove:cn} {\__graph_tl:nnn{graph}{#1}{vertices}} {#2}
\use:c{prop_#3remove:cn} {\__graph_tl:nnn{graph}{#1}{indegree}} {#2}
\use:c{prop_#3remove:cn} {\__graph_tl:nnn{graph}{#1}{outdegree}} {#2}
%%% decrement the vertex counter
%
\use:c{int_#3decr:c} {\__graph_tl:nnn{graph}{#1}{vertex-count}}
}
}
\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_item: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_item: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} {
\use:c{graph_#3put_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}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %
% % Test whether graph |#1| contains any cycles:
% %
% % \begin{macrocode}
% \prg_new_protected_conditional:Nnn \graph_get_cycle:NN
% {T, F, TF}
% % #1: graph id
% % #2: l3seq variable to put the cycle description in
% {
% \seq_clear:N #2
% \__graph_get_cycle:NNTF #1 #2
% {\prg_return_true: }
% {\prg_return_false:}
% }
%
% \prg_new_protected_conditional:Nnn \__graph_get_cycle:NN
% {T, F, TF}
% % #1: graph id
% % #2: l3seq variable
% {
% \graph_map_successors_inline:Nnn #1 {} {
% \seq_if_in:NnTF #2 {##1} {
% % TODO
% }{
% % TODO
% }
% }
% }
% % \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 vertices in the graph:
%
% \begin{macrocode}
\cs_new:Nn \graph_vertex_count:N {
\int_use:c {\__graph_tl:nnn{graph}{#1}{vertex-count}}
}
% \end{macrocode}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Get the number of edges leading out of vertex |#2|:
%
% \begin{macrocode}
\cs_new:Nn \graph_get_outdegree:Nn {
\prop_item: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_item: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_item: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_item: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_gclear_new:c {l__graph_source_vertices_(\int_use:N\g__graph_nesting_depth_int)_prop}
\prop_gclear_new:c {l__graph_tmp_indeg_(\int_use:N\g__graph_nesting_depth_int)_prop}
\graph_map_vertices_inline:Nn #1 {
\prop_put:cnf {l__graph_tmp_indeg_(\int_use:N\g__graph_nesting_depth_int)_prop} {##1}
{ \graph_get_indegree:Nn #1 {##1} }
\int_compare:nT {\graph_get_indegree:Nn #1 {##1} = 0} {
\prop_put:cnn {l__graph_source_vertices_(\int_use:N\g__graph_nesting_depth_int)_prop} {##1} {}
} }
%%% Main loop
%
\bool_until_do:nn {\prop_if_empty_p:c {l__graph_source_vertices_(\int_use:N\g__graph_nesting_depth_int)_prop}} {
%%% Choose any vertex (\l__graph_topo_key_tl, \l__graph_topo_value_tl)
%
\__graph_prop_any_key_pop:cN
{l__graph_source_vertices_(\int_use:N\g__graph_nesting_depth_int)_prop}
\l__graph_topo_key_tl
\graph_get_vertex:NVNT #1 \l__graph_topo_key_tl \l__graph_topo_val_tl {
%%% Deduct one from the counter of all affected nodes
%%% and add all now-empty vertices to source_vertices
%
\graph_map_outgoing_edges_inline:NVn #1 \l__graph_topo_key_tl {
\prop_put:cnf {l__graph_tmp_indeg_(\int_use:N\g__graph_nesting_depth_int)_prop} {##2}
{\int_eval:n {\prop_item:cn {l__graph_tmp_indeg_(\int_use:N\g__graph_nesting_depth_int)_prop} {##2} - 1}}
\int_compare:nT {\prop_item:cn {l__graph_tmp_indeg_(\int_use:N\g__graph_nesting_depth_int)_prop} {##2} = 0} {
\prop_put:cnn {l__graph_source_vertices_(\int_use:N\g__graph_nesting_depth_int)_prop} {##2} {}
} }
%%% Run the mapping funtion on the key and value from that vertex
%%% and manage the nesting depth counter
%
\int_gincr:N \g__graph_nesting_depth_int
\withargs:VVn \l__graph_topo_key_tl \l__graph_topo_val_tl
{ #2 {##1} {##2} }
\int_gdecr:N \g__graph_nesting_depth_int
}
} }
\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:nnn}
}
\tl_set:Nn #2 {\q_no_value} % TODO: test
}
\cs_generate_variant:Nn \__graph_prop_any_key_pop:NN {cN}
\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 {cnf}
\cs_generate_variant:Nn \graph_get_vertex:NnNT {NVNT}
\tl_new:N \l__graph_topo_key_tl
\tl_new:N \l__graph_topo_val_tl
\int_new:N \g__graph_nesting_depth_int
% \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} %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% We define some additional
% functions that can display the graph in table-form.
% This is the option-less version, which delegates
% to the full version:
%
% \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.
% Please note that with the standard options, the |xcolor| package
% is required with the |table| option, because of our use of the
% |\cellcolor| command.
%
% \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:n = true,
col_keys_format .tl_set:N = \l__graph_format_col_keys_tl,
col_keys_format .initial:n = \textbf,
col_keys_format .value_required:n = true,
vertex_vals_format .tl_set:N = \l__graph_format_vertex_vals_tl,
vertex_vals_format .initial:n = \use:n,
vertex_vals_format .value_required:n = true,
edge_vals_format .tl_set:N = \l__graph_format_edge_vals_tl,
edge_vals_format .initial:n = \use:n,
edge_vals_format .value_required:n = true,
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:n = true,
edge_direct_format .tl_set:N = \l__graph_format_edge_direct_tl,
edge_direct_format .initial:n = \cellcolor{green},
edge_direct_format .value_required:n = true,
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:n = true,
edge_none_format .tl_set:N = \l__graph_format_edge_none_tl,
edge_none_format .initial:n = {},
edge_none_format .value_required:n = true
}
% \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}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%