summaryrefslogtreecommitdiff
path: root/graphics/pgf/contrib/spectralsequences/src
diff options
context:
space:
mode:
Diffstat (limited to 'graphics/pgf/contrib/spectralsequences/src')
-rw-r--r--graphics/pgf/contrib/spectralsequences/src/spectralsequences.sty344
-rw-r--r--graphics/pgf/contrib/spectralsequences/src/sseqcheckdefinitions.code.tex242
-rw-r--r--graphics/pgf/contrib/spectralsequences/src/sseqdrawing.code.tex1556
-rw-r--r--graphics/pgf/contrib/spectralsequences/src/sseqforeach.code.tex266
-rw-r--r--graphics/pgf/contrib/spectralsequences/src/sseqkeys.code.tex2131
-rw-r--r--graphics/pgf/contrib/spectralsequences/src/sseqloadstore.code.tex194
-rw-r--r--graphics/pgf/contrib/spectralsequences/src/sseqmacromakers.code.tex529
-rw-r--r--graphics/pgf/contrib/spectralsequences/src/sseqmain.code.tex1966
-rw-r--r--graphics/pgf/contrib/spectralsequences/src/sseqmessages.code.tex585
-rw-r--r--graphics/pgf/contrib/spectralsequences/src/sseqparsers.code.tex1816
10 files changed, 9629 insertions, 0 deletions
diff --git a/graphics/pgf/contrib/spectralsequences/src/spectralsequences.sty b/graphics/pgf/contrib/spectralsequences/src/spectralsequences.sty
new file mode 100644
index 0000000000..e66aa5c780
--- /dev/null
+++ b/graphics/pgf/contrib/spectralsequences/src/spectralsequences.sty
@@ -0,0 +1,344 @@
+%%
+%% Package: spectralsequences v1.3.0 2021-07-18
+%% Author: Hood Chatham
+%% Email: hood@mit.edu
+%% Date: 2021-07-18
+%% License: Latex Project Public License
+%%
+%% A package for drawing spectral sequences
+%%
+
+
+% TODO:
+% Try catch blocks
+% Make an argument type for the \d page argument.
+% deal with xmin, xmax, etc (was there actually a problem we were trying to fix?)
+%
+% Redo sseqerrortest and set up regression test script as part of build (damn I didn't realize we'd lost anything imporant with that find -d disaster)
+% Maybe we should add some other regression tests too
+%
+% Lower priority:
+% error messages that should be warnings by default?
+%
+
+
+\NeedsTeXFormat{LaTeX2e}
+\ProvidesPackage{spectralsequences}[2021/07/18 v1.3.0]
+
+\RequirePackage{tikz}
+\RequirePackage{etoolbox}
+\RequirePackage{xparse}
+\RequirePackage{verbatim}
+
+\usetikzlibrary{quotes}
+\usetikzlibrary{fit}
+\usetikzlibrary{positioning}
+\usetikzlibrary{intersections}
+\usetikzlibrary{backgrounds}
+\usepgflibrary{arrows.meta}
+\usetikzlibrary{shapes}
+%\usetikzlibrary{patterns}
+%\usetikzlibrary{profiler}
+
+\newif\ifsseq@draftmode
+\newif\ifsseq@tooltip
+\DeclareOption{draft}{\sseq@draftmodetrue}
+\DeclareOption{tooltips}{\sseq@tooltiptrue}
+\ProcessOptions\relax
+
+\ifsseq@tooltip
+ \RequirePackage{pdfcomment}
+\else
+ \let\ulp@afterend\relax % So that removing the tooltip option doesn't cause the aux file to have an error.
+\fi
+
+\def\sseq@authorname{Hood Chatham}
+\def\sseq@authoremail{hood@mit.edu}
+
+
+% Commands we are going to expose just inside of environments
+\def\sseq@macrolist{%
+ \xcoord\ycoord\page %\xmin\xmax\ymin\ymax % these just get protected
+ % Defined in sseqmessages:
+ \quiet\endquiet
+ % These are defined in sseqmain:
+ \class\classoptions\replaceclass\replacesource\replacetarget
+ \replacestructlines
+ \d\doptions\kill\structline\structlineoptions\extension\extensionoptions\circleclasses
+ \lastlabel
+ % The following are defined in sseqparsers:
+ \pgfmathparse\isalive\lastx\lasty\lastclass\savestack\restorestack\pushstack\nameclass\tagclass
+ \parsecoordinate\parsedifferential\getdtarget\gettag
+ \IfOutOfBoundsTF\IfOutOfBoundsT\IfOutOfBoundsF\IfInBoundsTF\IfInBoundsT\IfInBoundsF
+ \IfExistsTF\IfExistsT\IfExistsF\IfAliveTF\IfAliveT\IfAliveF
+ \IfValidDifferentialTF\IfValidDifferentialT\IfValidDifferentialF
+ \DrawIfValidDifferentialTF\DrawIfValidDifferentialT\DrawIfValidDifferentialF\DrawIfValidDifferential
+ % sseqforeach
+ \Do\DoUntilOutOfBounds\DoUntilOutOfBoundsThenNMore
+}
+
+
+% All the tikz commands. We replace these in our environment too. Replacements defined in sseqparsers.
+\def\sseq@tikzcommands{%
+ \clip\coordinate\draw\fill\filldraw
+ \graph\matrix\node\path\pattern
+ \shade\shadedraw\useasboundingbox
+}
+
+
+%%%%%% %%%%%%
+%% %%
+%% Declarations and preliminaries %%
+%% %%
+%%%%%% %%%%%%
+\newif\ifsseq@inprogress
+
+\newif\ifsseq@hasname
+\newif\ifsseq@updateexisting
+\newif\ifsseq@ispageenv
+\newif\ifsseq@keepchanges
+\newif\ifsseq@keepglobaloptions
+\newif\ifsseq@globaldetone
+
+\newif\ifsseq@needstikz
+\newif\ifsseq@thispage
+\newif\ifsseq@outofrange
+\newif\ifsseq@classlabel
+\newif\ifsseq@draworphanedges
+\newif\ifsseq@draw
+\newif\ifsseq@drawedge
+\newif\ifsseq@tikzprims@integershift
+\newif\ifsseq@anchor
+\newif\ifsseq@rangecheck
+\newif\ifsseq@rangecheck@sideways
+
+\sseq@drawedgetrue
+\sseq@tikzprims@integershifttrue
+
+\newif\ifsseq@patchforeach
+\newif\ifsseq@patchfit
+\newif\ifsseq@patchxparseU
+
+\newif\ifsseq@tempif
+\newif\ifsseq@gtempif
+\newif\ifsseq@error
+
+\newtoks\sseq@temptoks
+\newtoks\sseq@scope@toks
+
+\newcount\sseq@thepagecount
+\newcount\sseq@anonsseqcount
+\newcount\sseq@x
+\newcount\sseq@y
+\newcount\sseq@tempcount
+\newcount\sseq@tempcountb
+\newcount\sseq@tempx
+\newcount\sseq@tempy
+\newcount\sseq@tempxb
+\newcount\sseq@tempyb
+\newcount\sseq@xoffset % We add these to everything to avoid overflow errors as much as possible
+\newcount\sseq@yoffset
+
+\newcount\sseq@stackdepth
+
+\newdimen\sseq@tempdimen
+\newdimen\sseq@tempxdimen
+\newdimen\sseq@tempydimen
+\newdimen\sseq@xscalecm
+\newdimen\sseq@yscalecm
+\newdimen\sseq@clip@xcenter
+\newdimen\sseq@clip@ycenter
+\newdimen\sseq@tooltip@height
+\newdimen\sseq@tooltip@width
+\newdimen\sseq@gridstrokethickness
+\sseq@gridstrokethickness=.1pt
+
+% Ensure \@xp and \@nx have the correct values in case that amsmath isn't loaded
+\let\@xp\expandafter
+\let\@nx\noexpand
+\def\@xptwo{\@xp\@xp\@xp}
+\def\@xpthree{\@xp\@xp\@xp\@xp\@xp\@xp\@xp}
+\def\sseq@nil{\sseq@thisshouldnthappen@nil unique expansion} % This expansion text should be unique so that \ifx\sseq@nil\othercommand is false.
+\def\sseq@infinity{10000} % Larger than any coordinate anyone will ever use.
+\newcount\sseq@infinitycount
+\sseq@infinitycount=\sseq@infinity\relax
+\def\sseq@macroname{\@xp\@gobble\string}
+\def\sseq@gobble@to@nil#1\sseq@nil{}
+\def\sseq@macrogobble#1->{}
+
+% These are only used by defertikzcommand now
+\def\sseq@callas#1{\def\sseq@callcmd{#1}}
+\def\sseq@call#1{\bgroup\@xp\let\sseq@callcmd#1\@xptwo\egroup\sseq@callcmd}
+
+\def\sseq@getfirstchar#1#2\sseq@nil{#1} % used in shift/checkshift transform
+\def\sseq@smuggle@macro#1#2\egroup{\@xp\egroup\@xp\def\@xp#1\@xp{#1}}
+
+
+\def\sseq@protected@edef{\let\sseq@store@slsl\\\def\\{\protect\\}\let\@@protect \protect \let \protect \@unexpandable@protect \afterassignment \sseq@restore@protect \edef}
+\def\sseq@protected@xdef{\let\sseq@store@slsl\\\def\\{\protect\\}\let\@@protect \protect \let \protect \@unexpandable@protect \afterassignment \sseq@restore@protect \xdef}
+\def\sseq@restore@protect{\let\protect\@@protect\let\\\sseq@store@slsl}
+
+\def\sseq@eval#1{\bgroup\edef\sseq@temp{#1}\@xp\egroup\sseq@temp}% I got this from sseq.sty
+\def\sseq@protectedeval#1{\bgroup\sseq@protected@edef\sseq@temp{#1}\@xp\egroup\sseq@temp}
+\def\sseq@eval@show#1{\bgroup\edef\sseq@temp{#1}\show\sseq@temp\@xp\egroup\sseq@temp}
+\def\sseq@profilenew#1#2{\pgfprofilenew{#1}\pretocmd#2{\pgfprofilestart{#1}}{}{\error}\apptocmd#2{\pgfprofileend{#1}}{}{\error}}
+
+%%%% add to macro commands
+\def\sseq@d@addto@macro#1#2{\@xp\def\@xp#1\@xp{#1#2}}
+\def\sseq@e@addto@macro#1#2{\edef#1{\unexpanded\@xp{#1}#2}} % let #2 be expanded
+\def\sseq@eo@addto@macro#1#2{\edef#1{\unexpanded\@xp{#1}\unexpanded\@xp{#2}}} % let #2 be expanded once
+\def\sseq@g@addto@macro#1#2{\@xp\gdef\@xp#1\@xp{#1#2}}
+\def\sseq@x@addto@macro#1#2{\xdef#1{\unexpanded\@xp{#1}#2}} % let #2 be expanded
+\def\sseq@xprotected@addto@macro#1#2{\sseq@protected@xdef#1{\unexpanded\@xp{#1}#2}}
+
+\def\sseq@d@addto@toks#1#2{#1\@xp{\the#1#2}}
+\def\sseq@e@addto@toks#1#2{\sseq@eval{#1{\the#1#2}}}
+
+\def\sseq@d@addto@temptoks{\sseq@d@addto@toks\sseq@temptoks}
+\def\sseq@e@addto@temptoks{\sseq@e@addto@toks\sseq@temptoks}
+
+
+% Used in sseqkeys to delete spaces from style commands.
+% Set the catcode of space to ignore, and then reparse the characters in #2.
+\def\sseq@setmacronospaces#1#2{%
+ \bgroup\catcode`\ =9\relax
+ \makeatletter
+ \scantokens{\expandafter\egroup\expandafter\def\expandafter#1\expandafter{\csname #2\endcsname}}%
+}
+
+\def\sseq@setnospaces#1#2{%
+ \bgroup\catcode`\ =9\relax
+ \makeatletter
+ \scantokens{\expandafter\egroup\expandafter\def\expandafter#1\expandafter{\@firstofone{#2}}}%
+}
+
+
+\def\sseq@removeparens{\@xp\sseq@removeparens@}
+\def\sseq@removeparens@(#1){#1}
+
+% Stolen from trimspaces.sty
+\bgroup
+\catcode`\Q=3
+\gdef\sseq@trimspaces#1{%
+ \romannumeral-`\q\sseq@trim@trim@\noexpand#1Q Q%
+}
+\long\gdef\sseq@trim@trim@#1 Q{\sseq@trim@trim@@#1Q}
+\long\gdef\sseq@trim@trim@@#1Q#2{#1}
+\egroup
+\def\sseq@trimleadingspaces{\romannumeral-`q}
+
+\def\sseq@ifempty#1{%
+ \@xp\ifx\@xp\sseq@nil\detokenize{#1}\sseq@nil
+ \@xp\@firstoftwo
+ \else
+ \@xp\@secondoftwo
+ \fi
+}
+
+\def\sseq@ifnil#1{
+ \ifx\sseq@nil#1
+ \@xp\@firstoftwo
+ \else
+ \@xp\@secondoftwo
+ \fi
+}
+
+\let\sseq@breakpoint\relax
+\let\sseq@breakpointfinally\@gobble
+\long\def\sseq@break#1\sseq@breakpoint{}
+\def\sseq@fbreak{\@xp\sseq@break\romannumeral-`0}
+\long\def\sseq@break@finally#1\sseq@breakpoint#2{#2}
+
+\long\def\sseq@breakfi{\fi\sseq@break}
+\long\def\sseq@breakfifi{\fi\fi\sseq@break}
+\long\def\sseq@breakfififi{\fi\fi\fi\sseq@break}
+\long\def\sseq@breakdataenv#1\end#2{
+ \def\sseq@tempa{sseqdata}\def\sseq@tempb{#2}\ifx\sseqtempa\sseqtempb
+ \@xp\sseq@breakdataenv@
+ \else
+ \@xp\sseq@breakdataenv
+ \fi
+}
+\def\sseq@breakdataenv@{\let\endsseqdata\sseq@breakendsseqdata\end{sseqdata}}
+
+\long\def\sseq@breakpageenv#1\end#2{
+ \def\sseq@tempa{sseqpage}\def\sseq@tempb{#2}\ifx\sseqtempa\sseqtempb
+ \@xp\sseq@breakpageenv@
+ \else
+ \@xp\sseq@breakpageenv
+ \fi
+}
+\def\sseq@breakpageenv@{\let\endsseqpage\sseq@breakendsseqpage\end{sseqpage}}
+\def\sseq@breakendsseqdata{}
+\def\sseq@breakendsseqpage{}
+
+\def\sseq@seteverythingtonoops{%
+ \let\sseqdata\comment
+ \let\sseqpage\comment
+ \let\sseqkeys\@gobble
+ \let\sseqnewgroup\@gobblethree
+}
+
+\AtBeginEnvironment{sideways}{\sseqset{range check sideways}}
+
+\input sseqmessages.code.tex % Exposes directly: \sseqerrortowarning
+\input sseqcheckdefinitions.code.tex
+
+\ifsseq@tempif\else % Set to false in checkdefinitions if it failed to patch the key-value system.
+ \sseq@seteverythingtonoops
+ \sseq@pgfkeyspatchfailed
+\fi
+
+\input sseqloadstore.code.tex % Responsible for installing environment-only macros
+\input sseqmacromakers.code.tex % Exposes directly: \DeclareSseqCommand, \NewSseqCommand, \DeclareSseqGroup, \NewSseqGroup
+
+\input sseqparsers.code.tex % Responsible for making tikz modifications, exposes directly \sseqnormalizemonomial, \sseqparseint
+
+
+
+\ifsseq@patchforeach
+ \input sseqforeach.code.tex
+\else
+ \def\sseq@patchfor{}
+\fi
+
+\input sseqkeys.code.tex % Exposes directly: \sseqset, \sseqnewfamily
+\input sseqmain.code.tex % Defines all the main commands. Exposes directly: the environments, \xmin, \xmax, etc, \SseqCopyPage
+\input sseqdrawing.code.tex
+
+% Give standard definitions for savedpaths wrappers
+\def\sseq@beginscope@object{\begin{scope}}
+\def\sseq@endscope@object{\end{scope}}
+\let\sseq@scope@object\@firstofone
+\let\sseq@style@object\@firstofone
+\let\sseq@class@object\sseq@class@draw@ifpage
+\let\sseq@differential@object\sseq@differential@draw@ifpage
+\let\sseq@structline@object\sseq@structline@draw@ifpage
+\let\sseq@extension@object\sseq@extension@draw@ifpage
+\let\sseq@circleclass@object\sseq@circleclass@draw@ifpage
+\let\sseq@tikzpath@object\@firstofone
+
+
+%%% Some default key settings
+\sseqset{
+ edge labels={auto=right},
+ classes={draw,circle,inner sep=0pt,minimum size=0.35em},
+ circle classes={newellipse, ellipse ratio=1.2,draw, inner sep=2pt},
+ edges=draw,
+ math nodes,
+ differentials=->,
+ pins=help lines
+}
+
+\ifsseq@tooltip
+ \let\sseqtooltip\sseq@tooltip@wrapper
+\fi
+
+% Extra commands to expose:
+\let\sseqifempty\sseq@ifempty
+\let\SseqParseInt\sseqparseint
+\let\SseqNewFamily\sseqnewfamily
+
+\def\sseqpower#1#2{\@xp\sseqtypesetpower@\@xp{\the\numexpr#2}{#1}{1}}
+\def\sseqpowerempty#1#2{\@xp\sseqtypesetpower@\@xp{\the\numexpr#2}{#1}{}}
+\def\sseqtypesetpower@#1#2#3{\ifnum#1=\z@#3\else\ifnum#1=\@ne#2\else#2^{#1}\fi\fi}
diff --git a/graphics/pgf/contrib/spectralsequences/src/sseqcheckdefinitions.code.tex b/graphics/pgf/contrib/spectralsequences/src/sseqcheckdefinitions.code.tex
new file mode 100644
index 0000000000..ad4570d5fa
--- /dev/null
+++ b/graphics/pgf/contrib/spectralsequences/src/sseqcheckdefinitions.code.tex
@@ -0,0 +1,242 @@
+%%
+%% Package: spectralsequences v1.3.0 2021-07-18
+%% Author: Hood Chatham
+%% Email: hood@mit.edu
+%% Date: 2021-07-18
+%% License: Latex Project Public License
+%%
+%% File: sseqcheckdefinitions.code.tex
+%%
+%% A few tikz commands are modified in sseqpages. This file checks the definitions of all commands modified. Of course, this is insufficient to ensure that
+%% the behavior isn't completely wrong, but at least it's a sanity check.
+%%
+
+% TODO: we could recover from failing to patch keys.
+
+\sseq@tempiftrue
+\begingroup
+\def\sseq@checkprotecteddef#1{\def\sseq@temp{\sseq@checkdef@{#1}}\afterassignment\sseq@temp\protected\long\@xp\def\csname sseq@check@\sseq@macroname#1\endcsname}
+\def\sseq@checkdef#1{\def\sseq@temp{\sseq@checkdef@{#1}}\afterassignment\sseq@temp\@xp\def\csname sseq@check@\sseq@macroname#1\endcsname}
+\def\sseq@checkdef@#1{
+ \@xp\ifx\csname sseq@check@\sseq@macroname#1\endcsname#1\else
+ \sseq@tempiffalse
+ \fi
+}
+
+%%
+%% sseqmacromakers.code.tex
+%%
+
+\ExplSyntaxOn
+% I should work out how old the version of xparse has to be before this breaks,
+% but I'm too lazy. It would also be nice if I could figure out how to ensure
+% that my trick of setting \l__cmd_grab_expandably_bool to false works
+% correctly, but this might be difficult
+\@ifpackagelater{xparse}{2017/02/08}{
+ \cs_if_exist:NTF \__xparse_grab_u:w {
+ \global\sseq@patchxparseUtrue
+ }{
+ \sseq@checkprotecteddef \__cmd_grab_u:w #1#2 \__cmd_run_code:{
+ \__cmd_grab_u_aux:nnN {#1} {#2} \cs_set_protected_nopar:Npn
+ }
+
+ \sseq@checkprotecteddef\__cmd_grab_u_aux:nnN #1#2#3 {
+ \tl_set:Nn \l__cmd_signature_tl {#2}
+ \exp_after:wN #3 \l__cmd_fn_tl ##1 #1
+ { \__cmd_add_arg:n {##1} }
+ \l__cmd_fn_tl
+ }
+
+
+ \sseq@checkprotecteddef \__cmd_normalize_type_u:w #1 {
+ \quark_if_recursion_tail_stop_do:nn {#1} { \__cmd_bad_arg_spec:wn }
+ \__cmd_normalize_check_lu:N u
+ \__cmd_add_arg_spec_mandatory:n { u {#1} } % Oct 17, 2018
+% \int_incr:N \l__cmd_mandatory_args_int
+% \tl_clear:N \l__cmd_last_delimiters_tl
+ \__cmd_normalize_arg_spec_loop:n
+ }
+
+ \ifsseq@tempif
+ \global\sseq@patchxparseUtrue
+ \else
+ \global\sseq@patchxparseUfalse
+ \fi
+ \sseq@tempiftrue
+ }
+}{}
+
+\@ifpackagelater{expl3}{2018/10/01}{
+ % TODO: Check that \peek_meaning and \peek_meaning_remove have the right definitions
+ \sseq@tempiftrue
+ \sseq@checkprotecteddef\peek_meaning_ignore_spaces:NTF#1#2#3{\peek_remove_spaces:n{\peek_meaning:NTF#1{#2}{#3}}}
+ \sseq@checkprotecteddef\peek_meaning_remove_ignore_spaces:NTF#1#2#3{\peek_remove_spaces:n{\peek_meaning_remove:NTF#1{#2}{#3}}}
+ \ifsseq@tempif\else
+ \sseq@error@n{expl3-incompatibility}{\peek_meaning_ignore_spaces:NTF}
+ \fi
+
+}{}
+
+\ExplSyntaxOff
+\sseq@tempiftrue
+%%
+%% sseqkeys.code.tex
+%%
+
+
+\sseq@checkdef\pgfkeys@case@one{%
+ \pgfkeysifdefined{\pgfkeyscurrentkey/.@cmd}%
+ {\pgfkeysgetvalue{\pgfkeyscurrentkey/.@cmd}{\pgfkeys@code}%
+ \expandafter\pgfkeys@code\pgfkeyscurrentvalue\pgfeov}
+ {\pgfkeys@case@two}%
+}
+
+\sseq@checkdef\pgfkeys@case@two@extern{%
+ \ifx\pgfkeyscurrentvalue\pgfkeysnovalue@text%
+ \pgfkeysvalueof{\pgfkeyscurrentkey}%
+ \else%
+ \pgfkeyslet{\pgfkeyscurrentkey}\pgfkeyscurrentvalue%
+ \fi%
+}
+
+
+\ifsseq@tempif\else
+ \endgroup
+ \sseq@tempiffalse % We'll throw a critical error in spectralsequences.sty to end input
+ \endinput
+\fi
+\sseq@tempiftrue
+%%
+%% sseqdrawing.code.tex
+%%
+
+%from \pgf\frontendlayer\tikz\libraries\tikzlibraryfit.code.tex line 81
+\sseq@checkdef\tikz@lib@fit@scan@handle#1{%
+ \iftikz@shapeborder%
+ % Ok, fit all four external anchors, if they exist
+ \tikz@lib@fit@adjust{\pgfpointanchor{\tikz@shapeborder@name}{west}}%
+ \tikz@lib@fit@adjust{\pgfpointanchor{\tikz@shapeborder@name}{east}}%
+ \tikz@lib@fit@adjust{\pgfpointanchor{\tikz@shapeborder@name}{north}}%
+ \tikz@lib@fit@adjust{\pgfpointanchor{\tikz@shapeborder@name}{south}}%
+ \else%
+ \tikz@lib@fit@adjust{#1}%
+ \fi%
+ \tikz@lib@fit@scan%
+}
+
+% from \pgf\frontendlayer\tikz\tikz.code.tex line 5164
+\sseq@checkdef\tikz@calc@anchor#1.#2\tikz@stop{%
+ \pgfpointanchor{\tikz@pp@name{#1}}{#2}%
+}
+
+\ifsseq@tempif
+ \global\sseq@patchfittrue
+\else
+ % New version of \tikz@calc@anchor
+ \sseq@tempiftrue
+ \sseq@checkdef\tikz@calc@anchor#1.#2\tikz@stop{%
+ \ifcsname pgf@sh@ns@\tikz@pp@name {#1}\endcsname
+ \pgfpointanchor {\tikz@pp@name {#1}}{#2}%
+ \else
+ \pgfpointanchor {#1}{#2}%
+ \fi
+ }
+ \ifsseq@tempif
+ \global\sseq@patchfittrue
+ \else
+ \sseq@warning{fit-patch-failed}
+ \global\sseq@patchfitfalse
+ \fi
+\fi
+
+\sseq@tempiftrue
+
+
+%%
+%% sseqforeach.code.tex
+%%
+\sseq@checkdef\pgffor@@vars@opt[#1]{\pgfkeys{/pgf/foreach/.cd,#1}\pgffor@vars}
+
+\sseq@checkdef\pgffor@doloop{%
+ \pgffor@begingroup
+ \edef\pgffor@temp{\expandafter\Pgffor@geT\the\pgffor@iter}%
+ \edef\pgffor@incheck{{.0/}{\pgffor@temp/}}%
+ \expandafter\pgfutil@in@\pgffor@incheck%
+ \ifpgfutil@in@%
+ \expandafter\pgffor@strip\pgffor@temp%
+ \fi%
+ \expandafter\pgfutil@in@\expandafter/\expandafter{\pgffor@var}%
+ \ifpgfutil@in@%
+ \expandafter\def\expandafter\pgffor@valuerest\expandafter{\pgffor@temp//\relax}%
+ \expandafter\pgffor@multiassign\pgffor@var/\pgffor@stop/\pgffor@stop/\relax%
+ \else%
+ % Convert to alphabetic sequence, if necessary.
+ \ifpgffor@alphabeticsequence%
+ \pgffor@makealphabetic\pgffor@temp%
+ \expandafter\let\pgffor@var=\pgffor@temp%
+ \else%
+ \expandafter\expandafter\expandafter\def\expandafter\pgffor@var\expandafter{\pgffor@temp}%
+ \fi%
+ \fi%
+ % Insert any context, if any.
+ \ifpgffor@context%
+ \let\pgffor@temp=\pgffor@dots@pre%
+ \expandafter\pgfutil@append@macrotomacro\expandafter%
+ {\expandafter\pgffor@temp\expandafter}\expandafter{\pgffor@var}%
+ \expandafter\pgfutil@append@macrotomacro\expandafter%
+ {\expandafter\pgffor@temp\expandafter}\expandafter{\pgffor@dots@post}%
+ \expandafter\let\pgffor@var=\pgffor@temp%
+ \fi%
+ % Perform assignments before loop body.
+ \ifx\pgffor@assign@before@code\pgfutil@empty%
+ \else%
+ \pgffor@assign@before@code%
+ \fi%
+ %
+ \expandafter\expandafter\expandafter\pgffor@reset@hooks\expandafter\pgffor@beginhook\expandafter\pgffor@body\pgffor@endhook%
+ %
+ % Perform assignments after loop body.
+ \ifx\pgffor@assign@after@code\pgfutil@empty%
+ \else%
+ \pgffor@assign@after@code%
+ \fi%
+ \pgffor@endgroup%
+ \pgffor@loop%
+}
+
+\sseq@checkdef\pgffor@invokebody{%
+ \pgffor@begingroup%
+ \expandafter\pgfutil@in@\expandafter/\expandafter{\pgffor@var}%
+ \ifpgfutil@in@%
+ \expandafter\def\expandafter\pgffor@valuerest\expandafter{\pgffor@value//\relax}%
+ \expandafter\pgffor@multiassign\pgffor@var/\pgffor@stop/\pgffor@stop/\relax%
+ \else%
+ \expandafter\expandafter\expandafter\def\expandafter\pgffor@var\expandafter{\pgffor@value}%
+ \fi%
+ % Execute assign once code.
+ \ifx\pgffor@assign@once@code\pgfutil@empty%
+ \else\pgffor@assign@once@code%
+ \fi%
+ % Execute assign before code.
+ \ifx\pgffor@assign@before@code\pgfutil@empty%
+ \else\pgffor@assign@before@code%
+ \fi%
+ %
+ \expandafter\expandafter\expandafter\pgffor@reset@hooks\expandafter\pgffor@beginhook\expandafter\pgffor@body\pgffor@endhook%
+ % Execute assign after code.
+ \ifx\pgffor@assign@after@code\pgfutil@empty%
+ \else%
+ \pgffor@assign@after@code%
+ \fi%
+ %
+ \pgffor@endgroup%
+}
+
+\ifsseq@tempif
+ \global\sseq@patchforeachtrue
+\else
+ \sseq@warning{foreach-patch-failed}
+ \global\sseq@patchforeachfalse
+\fi
+
+\endgroup \ No newline at end of file
diff --git a/graphics/pgf/contrib/spectralsequences/src/sseqdrawing.code.tex b/graphics/pgf/contrib/spectralsequences/src/sseqdrawing.code.tex
new file mode 100644
index 0000000000..db01b04481
--- /dev/null
+++ b/graphics/pgf/contrib/spectralsequences/src/sseqdrawing.code.tex
@@ -0,0 +1,1556 @@
+%%
+%% Package: spectralsequences v1.3.0 2021-07-18
+%% Author: Hood Chatham
+%% Email: hood@mit.edu
+%% Date: 2021-07-18
+%% License: Latex Project Public License
+%%
+%% File: sseqdrawing.code.tex
+%%
+%% Defines the macros that draw the features of the spectral sequence (at least, those that aren't drawn by tikz).
+%% Everything here has to be super optimized, because like 90% of the execution time is in these functions.
+%% In particular, tikz is a horrible performance hole that must be avoided at all costs. For example, the code
+%% the code to produce the axis ticks used to use the tikz \node command, and in the file example_KF3n.tex drawing
+%% the axes ticks was taking about 1/3 of the compile time (a little under 1s out of a 3.2)
+%%
+
+% Only for integer valued coordinates...
+\def\sseq@qpointxy#1#2{\pgfqpointxy{\numexpr#1+\sseq@xoffset\relax}{\numexpr#2+\sseq@yoffset\relax}}
+
+\def\sseq@transform@xymirror{
+ \pgfsetxvec{\pgfqpoint{1cm}{0cm}}
+ \pgfsetyvec{\pgfqpoint{0cm}{1cm}}
+ \pgftransformcm{0}{1}{1}{0}{\pgfpointxy{\sseq@xoffset*\sseq@xscale }{\sseq@yoffset*\sseq@yscale}} % Reflect x and y axes,
+ \pgftransformshift{\pgfpointxy{-\sseq@xoffset*\sseq@yscale}{-\sseq@yoffset*\sseq@xscale}} % but we need to conjugate by a shift of (xoffset,yoffset)
+ \pgfsetyvec{\pgfqpoint{0cm}{\sseq@xscale cm}}
+ \pgfsetxvec{\pgfqpoint{\sseq@yscale cm}{0cm}}
+}
+
+
+\def\sseq@setlayoutparameter#1#2{\@xp\let\csname sseq@#1\@xp\endcsname\csname sseq@#1@#2\endcsname}
+\def\sseq@setlayoutparameters{
+ \sseq@setlayoutparameter{yaxisorigin}{x\sseq@xaxistype}
+ \sseq@setlayoutparameter{drawyaxis}{x\sseq@xaxistype}
+ \sseq@setlayoutparameter{yaxisstartoffset}{x\sseq@xaxistype}
+ \sseq@setlayoutparameter{yaxisendoffset}{x\sseq@xaxistype}
+ \sseq@setlayoutparameter{bottomgridpadding}{x\sseq@xaxistype}
+ \sseq@setlayoutparameter{topgridpadding}{x\sseq@xaxistype}
+ \sseq@setlayoutparameter{bottomclippadding}{x\sseq@xaxistype}
+ \sseq@setlayoutparameter{topclippadding}{x\sseq@xaxistype}
+ \sseq@setlayoutparameter{xlabelposition}{x\sseq@xaxistype}
+ \sseq@setlayoutparameter{totalverticalmargin}{x\sseq@xaxistype}
+%
+ \sseq@setlayoutparameter{xaxisorigin}{y\sseq@yaxistype}
+ \sseq@setlayoutparameter{drawxaxis}{y\sseq@yaxistype}
+ \sseq@setlayoutparameter{xaxisstartoffset}{y\sseq@yaxistype}
+ \sseq@setlayoutparameter{xaxisendoffset}{y\sseq@yaxistype}
+ \sseq@setlayoutparameter{leftgridpadding}{y\sseq@yaxistype}
+ \sseq@setlayoutparameter{rightgridpadding}{y\sseq@yaxistype}
+ \sseq@setlayoutparameter{leftclippadding}{y\sseq@yaxistype}
+ \sseq@setlayoutparameter{rightclippadding}{y\sseq@yaxistype}
+ \sseq@setlayoutparameter{ylabelposition}{y\sseq@yaxistype}
+ \sseq@setlayoutparameter{totalhorizontalmargin}{y\sseq@yaxistype}
+}
+
+% The appropriate one of these is chosen by the "axis type" keys in sseqkeys.
+
+% Used to calculate available area in range check
+\def\sseq@totalhorizontalmargin@yborder{(\sseq@xaxis@tail + \sseq@yaxisgap + \sseq@xaxis@end@extend + \sseq@clip@padding@right)}
+\def\sseq@totalhorizontalmargin@yframe{(\sseq@ylabelgap + \sseq@maxylabelwidth + \sseq@yaxisgap * 2)}
+\def\sseq@totalhorizontalmargin@ycenter{(\sseq@xaxis@start@extend + \sseq@clip@padding@left + \sseq@xaxis@end@extend + \sseq@clip@padding@right)}
+\def\sseq@totalhorizontalmargin@ynone{(\sseq@xaxis@start@extend + \sseq@clip@padding@left + \sseq@xaxis@end@extend + \sseq@clip@padding@right)}
+
+\def\sseq@totalverticalmargin@xborder{(\sseq@yaxis@tail + \sseq@xaxisgap + \sseq@yaxis@end@extend + \sseq@clip@padding@top)}
+\def\sseq@totalverticalmargin@xframe{(\sseq@xlabelgap + \sseq@xlabelheight + \sseq@xaxisgap * 2)}
+\def\sseq@totalverticalmargin@xcenter{(\sseq@yaxis@start@extend + \sseq@clip@padding@bottom + \sseq@yaxis@end@extend + \sseq@clip@padding@top)}
+\def\sseq@totalverticalmargin@xnone{(\sseq@yaxis@start@extend + \sseq@clip@padding@bottom + \sseq@yaxis@end@extend + \sseq@clip@padding@top)}
+
+% Used to determine axis and ticks placement
+\def\sseq@xaxisorigin@yborder{\sseq@xmin}
+\def\sseq@xaxisorigin@yframe{\sseq@xmin}
+\def\sseq@xaxisorigin@ycenter{\sseq@xaxisorigin@center@}
+\def\sseq@yaxisorigin@xborder{\sseq@ymin}
+\def\sseq@yaxisorigin@xframe{\sseq@ymin}
+\def\sseq@yaxisorigin@xcenter{\sseq@yaxisorigin@center@}
+
+% Used to determine clipping and grid boundaries
+\def\sseq@leftgridpadding@yborder{\dimexpr\sseq@yaxisgap-\sseq@yclip@axisgap\relax}
+\def\sseq@rightgridpadding@yborder{\sseq@xaxis@end@extend}
+\def\sseq@leftgridpadding@ycenter{\sseq@xaxis@start@extend}
+\def\sseq@rightgridpadding@ycenter{\sseq@xaxis@end@extend}
+\def\sseq@leftgridpadding@yframe{\dimexpr\sseq@yaxisgap-\sseq@yclip@axisgap\relax}
+\def\sseq@rightgridpadding@yframe{5cm} % Just make it big enough to push off the end of the clip
+\def\sseq@leftgridpadding@ynone{\sseq@yaxisgap}
+\def\sseq@rightgridpadding@ynone{\sseq@yaxisgap}
+
+
+\def\sseq@bottomgridpadding@xborder{\dimexpr\sseq@xaxisgap-\sseq@xclip@axisgap\relax}
+\def\sseq@topgridpadding@xborder{\sseq@yaxis@end@extend}
+\def\sseq@bottomgridpadding@xcenter{\sseq@yaxis@start@extend}
+\def\sseq@topgridpadding@xcenter{\sseq@yaxis@end@extend}
+\def\sseq@bottomgridpadding@xframe{\dimexpr\sseq@xaxisgap-\sseq@xclip@axisgap\relax}
+\def\sseq@topgridpadding@xframe{5cm}
+\def\sseq@bottomgridpadding@xnone{\sseq@xaxisgap}
+\def\sseq@topgridpadding@xnone{\sseq@xaxisgap}
+
+\def\sseq@leftclippadding@yborder{\dimexpr-\sseq@yaxisgap+\sseq@yclip@axisgap\relax}
+\def\sseq@rightclippadding@yborder{\dimexpr\sseq@xaxis@end@extend+\sseq@clip@padding@right\relax}
+\def\sseq@leftclippadding@ycenter{\dimexpr-\sseq@xaxis@start@extend-\sseq@clip@padding@left\relax}
+\def\sseq@rightclippadding@ycenter{\dimexpr\sseq@xaxis@end@extend+\sseq@clip@padding@right\relax}
+\def\sseq@leftclippadding@yframe{\dimexpr-\sseq@yaxisgap+\sseq@yclip@axisgap\relax}
+\def\sseq@rightclippadding@yframe{\dimexpr\sseq@yaxisgap-\sseq@yclip@axisgap\relax}
+\def\sseq@leftclippadding@ynone{\dimexpr-\sseq@yaxisgap-\sseq@clip@padding@left\relax}
+\def\sseq@rightclippadding@ynone{\dimexpr\sseq@yaxisgap+\sseq@clip@padding@right\relax}
+
+\def\sseq@bottomclippadding@xborder{\dimexpr-\sseq@xaxisgap+\sseq@xclip@axisgap\relax}
+\def\sseq@topclippadding@xborder{\dimexpr\sseq@yaxis@end@extend+\sseq@clip@padding@top\relax}
+\def\sseq@bottomclippadding@xcenter{\dimexpr-\sseq@yaxis@start@extend-\sseq@clip@padding@bottom\relax}
+\def\sseq@topclippadding@xcenter{\dimexpr\sseq@yaxis@end@extend+\sseq@clip@padding@top\relax}
+\def\sseq@bottomclippadding@xframe{\dimexpr-\sseq@xaxisgap+\sseq@xclip@axisgap\relax}
+\def\sseq@topclippadding@xframe{\dimexpr\sseq@xaxisgap-\sseq@xclip@axisgap\relax}
+\def\sseq@bottomclippadding@xnone{\dimexpr-\sseq@xaxisgap-\sseq@clip@padding@bottom\relax}
+\def\sseq@topclippadding@xnone{\dimexpr\sseq@xaxisgap+\sseq@clip@padding@top\relax}
+
+% Used to figure out how much further beyond (min -- max) to draw axes
+\def\sseq@xaxisstartoffset@yborder{\dimexpr\sseq@xaxis@tail+\sseq@yaxisgap\relax}
+\def\sseq@xaxisendoffset@yborder{\sseq@xaxis@end@extend}
+\def\sseq@xaxisstartoffset@ycenter{\sseq@xaxis@start@extend}
+\def\sseq@xaxisendoffset@ycenter{\sseq@xaxis@end@extend}
+\def\sseq@xaxisstartoffset@yframe{\sseq@yaxisgap}
+\def\sseq@xaxisendoffset@yframe{\sseq@yaxisgap}
+
+\def\sseq@yaxisstartoffset@xborder{\dimexpr\sseq@yaxis@tail+\sseq@xaxisgap\relax}
+\def\sseq@yaxisendoffset@xborder{\sseq@yaxis@end@extend}
+\def\sseq@yaxisstartoffset@xcenter{\sseq@yaxis@start@extend}
+\def\sseq@yaxisendoffset@xcenter{\sseq@yaxis@end@extend}
+\def\sseq@yaxisstartoffset@xframe{\sseq@xaxisgap}
+\def\sseq@yaxisendoffset@xframe{\sseq@xaxisgap}
+
+\def\sseq@xlabelposition@xborder{(0,-\sseq@xaxisstartoffset)}
+\def\sseq@xlabelposition@xcenter{(-\sseq@yaxisgap,-\sseq@xaxisstartoffset-5pt)}
+\def\sseq@xlabelposition@xframe{(0,-\sseq@xaxisgap-\sseq@xlabelgap-10pt)}
+\def\sseq@xlabelposition@xnone{(0,0)}
+
+\def\sseq@ylabelposition@yborder{(0,\sseq@yaxisstartoffset)}
+\def\sseq@ylabelposition@ycenter{(-\sseq@xaxisgap,\sseq@yaxisstartoffset+5pt)}
+\def\sseq@ylabelposition@yframe{(0,\sseq@yaxisgap+\sseq@ylabelgap+10pt)}
+\def\sseq@ylabelposition@ynone{(0,0)}
+%%%
+%%% Draw axes and clip
+%%%
+\def\sseq@handlexaxis{
+ \bgroup
+ \sseq@drawxaxis
+ \ifsseq@drawxaxisticks
+ \sseq@drawxticks
+ \fi
+ \egroup
+}
+\def\sseq@handleyaxis{
+ \bgroup
+ \sseq@drawyaxis
+ \ifsseq@drawyaxisticks
+ \sseq@drawyticks
+ \fi
+ \egroup
+}
+
+% Draws an x axis. Use with \sseq@transform@xymirror to draw y axis.
+% #1 -- horizontal start offset
+% #2 -- horizontal end offset
+% #3 -- vertical offset
+% #4 -- start value
+% #5 -- end value
+% #6 -- vertical value
+\def\sseq@drawaxis@generic#1#2#3#4#5#6{
+ \bgroup
+ \pgftransformshift{\pgfqpoint{#2}{#3}}
+ \pgfpathmoveto{\sseq@qpointxy{#5}{#6}}
+ \egroup
+%
+ \bgroup
+ \pgftransformshift{\pgfqpoint{#1}{#3}}
+ \pgfpathlineto{\sseq@qpointxy{#4}{#6}}
+ \egroup
+}
+
+\def\sseq@drawxaxis@ynone{
+ \sseq@drawxaxisticksfalse
+}
+\def\sseq@drawyaxis@xnone{
+ \sseq@drawyaxisticksfalse
+}
+
+\def\sseq@drawxaxis@yborder{
+ \sseq@drawaxis@generic{-\sseq@xaxisstartoffset}{\sseq@xaxisendoffset}{-\sseq@xaxisgap}{\sseq@xmin}{\sseq@xmax}{\sseq@yaxisorigin}
+ \pgfusepath{stroke}
+}
+\let\sseq@drawxaxis@ycenter\sseq@drawxaxis@yborder
+
+\def\sseq@drawxaxis@yframe{
+ \sseq@drawaxis@generic{-\sseq@xaxisstartoffset}{\sseq@xaxisendoffset}{-\sseq@xaxisgap}{\sseq@xmin}{\sseq@xmax}{\sseq@ymin}
+ \sseq@drawaxis@generic{-\sseq@xaxisstartoffset}{\sseq@xaxisendoffset}{ \sseq@xaxisgap}{\sseq@xmin}{\sseq@xmax}{\sseq@ymax}
+ \pgfusepath{stroke}
+}
+\def\sseq@drawyaxis@xborder{
+ \bgroup
+ \sseq@transform@xymirror
+ \sseq@drawaxis@generic{-\sseq@yaxisstartoffset}{\sseq@yaxisendoffset}{-\sseq@yaxisgap}{\sseq@ymin}{\sseq@ymax}{\sseq@xaxisorigin}
+ \pgfusepath{stroke}%
+ \egroup
+}
+\let\sseq@drawyaxis@xcenter\sseq@drawyaxis@xborder
+\def\sseq@drawyaxis@xframe{
+ \bgroup
+ \sseq@transform@xymirror
+ \sseq@drawaxis@generic{-\sseq@yaxisstartoffset}{\sseq@yaxisendoffset}{-\sseq@yaxisgap}{\sseq@ymin}{\sseq@ymax}{\sseq@xmin}
+ \sseq@drawaxis@generic{-\sseq@yaxisstartoffset}{\sseq@yaxisendoffset}{ \sseq@yaxisgap}{\sseq@ymin}{\sseq@ymax}{\sseq@xmax}
+ \pgfusepath{stroke}%
+ \egroup
+}
+
+\ExplSyntaxOn
+\def\sseq@intdivceiling#1#2{%
+ \ifnum#1>\z@ % \int_div_truncate:nn rounds towards 0. We want \int_div_ceiling
+ \int_div_truncate:nn{#1+#2-1}{#2} % if positive, use ceil(p/q)=floor((p+q-1)/q) for p,q integers
+ \else
+ \int_div_truncate:nn{#1}{#2} % if we are negative, towards 0 is ceiling
+ \fi
+}
+\def\sseq@intdivfloor#1#2{%
+ \ifnum#1<\z@ % \int_div_truncate:nn rounds towards 0. We want \int_div_floor
+ \int_div_truncate:nn{#1-#2+1}{#2} % if we are negative, use floor(p/q) = ceil(p-q+1/q) for p,q integers
+ \else
+ \int_div_truncate:nn{#1}{#2} % if positive, towards 0 is floor
+ \fi
+}
+\ExplSyntaxOff
+
+% #1 -- xmin
+% #2 -- xmax
+% #3 -- step
+% #4 -- offset
+% #5 -- ymin
+% #6 -- xaxisgap
+% #7 -- code
+\def\sseq@tickloop@generic#1#2#3#4#5#6#7{
+ \sseq@tempcount=\numexpr % min
+ \sseq@intdivceiling{#1}{#3} * #3
+ -
+ \sseq@intdivceiling{#4}{#3} * #3 + #4
+ \relax
+ \ifnum\sseq@tempcount<#1\relax
+ \advance\sseq@tempcount#3\relax
+ \fi
+ \sseq@tempcountb=\numexpr#2+1\relax % max
+ \loop
+ \bgroup
+ \pgftransformshift{\sseq@qpointxy{\sseq@tempcount}{#5}}%
+ \pgftransformshift{\pgfqpoint{0pt}{#6}}
+ #7
+ \egroup
+ \advance\sseq@tempcount#3\relax
+ \ifnum\sseq@tempcount<\sseq@tempcountb\repeat
+}
+
+
+\def\sseq@drawxticks{%
+ \sseq@tickloop@generic{\sseq@xmin}{\sseq@xmax}{\sseq@xtickstep}{\sseq@xtickstepoffset}{\sseq@yaxisorigin}{-\sseq@xaxisgap}{
+ \pgftransformshift{\pgfqpoint{0pt}{-\sseq@xlabelgap}}
+ \pgftransformresetnontranslations
+ %\@xp\tikzset\@xp{\sseq@xtickstyle}
+ %\pgftext{\tikz@options\tikz@textfont\hbox{$\sseq@xtickfn{\the\sseq@tempx}$}}%
+ \@xp\node\@xp[\sseq@xtickstyle]{\hbox{$\sseq@xtickfn{\the\sseq@tempcount}$}};
+ }
+ \ifnum\sseq@xmajortickstep>\z@
+ \sseq@tickloop@generic{\sseq@xmin}{\sseq@xmax}{\sseq@xmajortickstep}{\sseq@xtickstepoffset}{\sseq@yaxisorigin}{-\sseq@xaxisgap}{
+ \pgfpathmoveto{\pgfpointorigin}
+ \pgfpathlineto{\pgfpoint{0}{-0.4*\sseq@xaxisgap}}
+ }
+ \fi
+ \ifnum\sseq@xminortickstep>\z@
+ \sseq@tickloop@generic{\sseq@xmin}{\sseq@xmax}{\sseq@xminortickstep}{\sseq@xtickstepoffset}{\sseq@yaxisorigin}{-\sseq@xaxisgap}{
+ \pgfpathmoveto{\pgfpointorigin}
+ \pgfpathlineto{\pgfpoint{0}{-0.2*\sseq@xaxisgap}}
+ }
+ \fi
+ \pgfusepath{stroke}
+}
+\def\sseq@drawyticks{%
+ \sseq@transform@xymirror
+ \sseq@tickloop@generic{\sseq@ymin}{\sseq@ymax}{\sseq@ytickstep}{\sseq@ytickstepoffset}{\sseq@xaxisorigin}{-\sseq@yaxisgap}{
+ \pgftransformshift{\pgfqpoint{0pt}{-\sseq@ylabelgap}}
+ \pgftransformresetnontranslations
+ %\@xp\tikzset\@xp{\sseq@ytickstyle}
+ %\pgftext{\tikz@options\tikz@textfont\hbox{$\sseq@ytickfn{\the\sseq@tempx}$}}%
+ \@xp\node\@xp[\sseq@ytickstyle]{\hbox{$\sseq@ytickfn{\the\sseq@tempcount}$}};
+ }
+ \ifnum\sseq@ymajortickstep>\z@
+ \sseq@tickloop@generic{\sseq@ymin}{\sseq@ymax}{\sseq@ymajortickstep}{\sseq@ytickstepoffset}{\sseq@xaxisorigin}{-\sseq@yaxisgap}{
+ \pgfpathmoveto{\pgfpointorigin}
+ \pgfpathlineto{\pgfpoint{0}{-0.4*\sseq@yaxisgap}}
+ }
+ \fi
+ \ifnum\sseq@yminortickstep>\z@
+ \sseq@tickloop@generic{\sseq@ymin}{\sseq@ymax}{\sseq@yminortickstep}{\sseq@ytickstepoffset}{\sseq@xaxisorigin}{-\sseq@yaxisgap}{
+ \pgfpathmoveto{\pgfpointorigin}
+ \pgfpathlineto{\pgfpoint{0}{-0.2*\sseq@yaxisgap}}
+ }
+ \fi
+ \pgfusepath{stroke}
+}
+
+
+\def\sseq@setupclip{
+ %\clip(\sseq@xmin-0.4,\sseq@ymin-0.4) rectangle (\sseq@xmax+0.5,\sseq@ymax+0.5);%
+ \ifsseq@clip
+ \ifx\sseq@customclip\pgfutil@empty
+ \bgroup
+ \def\xmin{\pgftransformshift{\pgfqpoint{\sseq@leftclippadding}{0pt}}}
+ \def\ymin{\pgftransformshift{\pgfqpoint{0pt}{\sseq@bottomclippadding}}}
+ \def\xmax{\pgftransformshift{\pgfqpoint{\sseq@rightclippadding}{0pt}}}
+ \def\ymax{\pgftransformshift{\pgfqpoint{0pt}{\sseq@topclippadding}}}
+ \bgroup
+ \xmin\ymin
+ \pgfpathmoveto{\pgfqpointxy{\numexpr\sseq@xmin+\sseq@xoffset\relax}{\numexpr\sseq@ymin+\sseq@yoffset\relax}}
+ \egroup
+ \bgroup
+ \xmin\ymax
+ \pgfpathlineto{\pgfqpointxy{\numexpr\sseq@xmin+\sseq@xoffset\relax}{\numexpr\sseq@ymax+\sseq@yoffset\relax}}
+ \egroup
+ \bgroup
+ \xmax\ymax
+ \pgfpathlineto{\pgfqpointxy{\numexpr\sseq@xmax+\sseq@xoffset\relax}{\numexpr\sseq@ymax+\sseq@yoffset\relax}}
+ \egroup
+ \bgroup
+ \xmax\ymin
+ \pgfpathlineto{\pgfqpointxy{\numexpr\sseq@xmax+\sseq@xoffset\relax}{\numexpr\sseq@ymin+\sseq@yoffset\relax}}
+ \egroup
+ \egroup
+ \pgfpathclose
+ \pgfgetpath\sseq@theclippath % This stores the clipping so I can apply it later
+ \pgfusepath{discard}% This has to be after the egroup or else the clipping gets screwed up
+%
+ \bgroup
+ \pgfpathmoveto{\pgfqpointxy{\numexpr\sseq@xmin+\sseq@xoffset\relax}{\numexpr\sseq@ymin+\sseq@yoffset\relax}}
+ \pgfpathlineto{\pgfqpointxy{\numexpr\sseq@xmin+\sseq@xoffset\relax}{\numexpr\sseq@ymax+\sseq@yoffset\relax}}
+ \pgfpathlineto{\pgfqpointxy{\numexpr\sseq@xmax+\sseq@xoffset\relax}{\numexpr\sseq@ymax+\sseq@yoffset\relax}}
+ \pgfpathlineto{\pgfqpointxy{\numexpr\sseq@xmax+\sseq@xoffset\relax}{\numexpr\sseq@ymin+\sseq@yoffset\relax}}
+ \pgfpathclose
+ \egroup
+ \pgfgetpath\sseq@therangepath % Only for deciding whether to draw "tricky edges"
+ \pgfusepath{discard}
+ \else
+ \def\sseq@temp{\path[name path=temp]}
+ \@xptwo\sseq@temp\@xp\@gobble\sseq@customclip
+ \pgfgetpath\sseq@theclippath
+ \let\sseq@theclippath\tikz@intersect@path@name@temp
+ \fi
+ \else
+ \let\sseq@theclippath\relax
+ \fi
+}
+\def\sseq@useclip{\ifx\sseq@theclippath\relax\else\pgfsetpath\sseq@theclippath\pgfusepath{clip}\fi}
+
+
+% sets \pgf@xa and \pgf@ya equal to the coordinates of (1,1), sets \pgf@xb and \pgf@xb equal to (x grid step, y grid step)
+\def\sseq@grid@setstepandgridstep{
+ \pgf@process{\pgfqpointxy{1}{1}}
+ \pgf@xa=\pgf@x
+ \pgf@ya=\pgf@y
+ \pgf@process{\pgfqpointxy{\sseq@xgridstep}{\sseq@ygridstep}}
+ \pgf@xb=\pgf@x
+ \pgf@yb=\pgf@y
+}
+
+% #1 -- macro to store result in
+% #2 -- "x" or "y" as appropriate
+% #3 -- length (with units)
+% #4 -- fraction of gridstep
+% If #2 is x, then it either sets #1 to #3/(x scale) or xgridstep*#4, whichever is smaller
+% The output is intended for use with \pgfpointxy.
+\def\sseq@grid@atmostgridstep#1#2#3#4{
+ \ifdim#3<\dimexpr\csname pgf@#2b\endcsname*#4\relax
+ \pgfmathparse{#3/\csname pgf@#2a\endcsname}
+ \let#1\pgfmathresult
+ \else
+ \edef#1{\csname sseq@#2gridstep\endcsname*#4}
+ \fi
+}
+
+% This grid was a huge pain in the ass to get right...
+\def\sseq@grid@chess{
+ \bgroup
+ \pgfscope
+ % Because of complicated aliasing issues that arise when misusing the \pgfgrid command in this way,
+ % it's more convenient to add a clip than to actually stop things in the right place.
+ \clip (\sseq@xmin-0.49,\sseq@ymin-0.49)
+ rectangle (\sseq@xmax+0.49,\sseq@ymax+0.49);
+% \clip (\sseq@xmin,\sseq@ymin) ++ (-0.49, -0.49)
+% rectangle ([shift={(0.5cm*\sseq@yscale,0.5*\sseq@xscale)}]
+% \sseq@xmax,\sseq@ymax);
+%
+ \sseq@useclip
+ \pgfsetcolor{\sseq@gridcolor}
+ \pgfsetstrokeopacity{0.3} % We don't set low opacity for the other grids, but this one is visible even if it is very faint.
+ \sseq@grid@setstepandgridstep
+%
+% Use ifdim with cm to compare real scalars b/c tex is dumb.
+ % okay, now we need to be careful in order to avoid overflows. The hack we are using requires that the xvec and the yvec are equal
+ % because we can't apply these via lowlevelsynccm. However, xvec and yvec are the things that prevent overflows.
+ % What we need to do is set them both equal to the smaller value. Now the scale factor determines the size of the squares,
+ % so we mix in the grid steps. We also need to scale the larger scaled coordinate by the ratio of the two scales.
+ \ifdim\sseq@xscale cm<\sseq@yscale cm\relax
+ % x is smaller so set both x and y vecs to \sseq@xscale cm.
+ \pgfsetxvec{\pgfpoint{\sseq@xscale cm}{0cm}}
+ \pgfsetyvec{\pgfpoint{0cm}{\sseq@xscale cm}}
+ \def\sseq@xadjustscale{\sseq@xgridstep}
+ \def\sseq@yadjustscale{\sseq@yscale/\sseq@xscale*\sseq@ygridstep}
+ \def\sseq@stepscale{\sseq@xscale}
+ \else
+ \pgfsetxvec{\pgfpoint{\sseq@yscale cm}{0cm}}
+ \pgfsetyvec{\pgfpoint{0cm}{\sseq@yscale cm}}
+ \def\sseq@xadjustscale{(\sseq@xscale/\sseq@yscale*\sseq@xgridstep)}
+ \def\sseq@yadjustscale{\sseq@ygridstep}
+ \def\sseq@stepscale{\sseq@yscale}
+ \fi
+ \pgftransformxscale{\sseq@xadjustscale}
+ \pgftransformyscale{\sseq@yadjustscale}
+ % Now we have to put in some ridiculously complicated shift in order to get the bottom corner in the right place.
+ % I found this formula mostly by trial and error.
+ % The basic idea that we should be making something divisible by twice the grid step (which is the actual period of our grid)
+ % is reasonable enough, but what the heck are these other two terms for? I don't know either, thank god it works.
+ \pgftransformshift{\pgfpointxy{
+ floor((\sseq@xgridstep-1)/2)/\sseq@xgridstep
+ + \ifodd\sseq@xgridstep\space 0 \else 0.5/\sseq@xgridstep\fi
+ + mod(1+\sseq@xmin/\sseq@xgridstep,2*\sseq@xgridstep)
+ }{
+ floor((\sseq@ygridstep-1)/2)/\sseq@ygridstep
+ + \ifodd\sseq@ygridstep\space 0\else 0.5/\sseq@ygridstep\fi
+ + mod(1+\sseq@ymin/\sseq@ygridstep,2*\sseq@ygridstep)
+ }
+ }
+ % We use lowlevelsynccm to make the scale adjust the line width. Normally scaling doesn't affect line width, text size, etc, which is sensible.
+ % When you apply \pgflowlevelsynccm, pgf loses track of the coordinate matrix and it gets indiscriminately applied to everything.
+ % This is good for us, because we're doing this absurd thing where we make a checkerboard by drawing a really really fat line.
+ \pgflowlevelsynccm
+ \pgfsetlinewidth{1cm*\sseq@stepscale}
+ \pgfsetdash{{1cm*\sseq@stepscale}{1cm*\sseq@stepscale}}{1cm*\sseq@stepscale}
+%
+ % Naturally some trial and error occurred here too.
+ % Note the huge multiples of \sseq@xgridstep I added in -- they probably aren't necessary, but I don't understand what's going on so whatever. They make sure that the pattern always extends past the edge of the clipping region. Otherwise the gird occasionally stops short for some reason.
+ % The basic idea is that things need to be divisible by twice the grid step. The part I added in the shift above
+ % was the remainder when you divide \sseq@xmin by twice the grid step, now we need to add in the multiple of twice the grid step.
+ % The -0.5 is ensuring that the checkerboard boundaries lie at half-integer coordinates (I think).
+ \pgfpathgrid[stepx= 2cm*\sseq@stepscale,stepy=2cm*\sseq@stepscale]
+ { \pgfpointxy
+ { -0.5 + \sseq@intdivfloor{\numexpr\sseq@xmin - \sseq@xgridstep\relax}{2*\sseq@xgridstep}*2*\sseq@xgridstep + \sseq@xoffset -50*\sseq@xgridstep
+ }
+ { -0.5 + \sseq@intdivfloor{\numexpr\sseq@ymin - \sseq@ygridstep\relax}{2*\sseq@ygridstep}*2*\sseq@ygridstep + \sseq@yoffset -50*\sseq@ygridstep
+ }
+ }{ \pgfpointxy
+ { \sseq@xmax + \sseq@xoffset + 50*\sseq@xgridstep }
+ { \sseq@ymax + \sseq@yoffset + 50*\sseq@ygridstep }
+ }
+ \pgfusepath{stroke}
+ \endpgfscope
+ \egroup
+}
+
+\def\sseq@grid@crossword{
+ \bgroup
+ \pgfscope
+ \sseq@useclip
+ \pgfsetcolor{\sseq@gridcolor}
+ \pgfsetlinewidth{\the\sseq@gridstrokethickness}
+ \sseq@grid@setstepandgridstep
+%
+ % We don't want the grid to end with an extra line, so extend it by one half the step distance or
+ % \sseq@---gridpadding, whichever is shorter.
+ \sseq@grid@atmostgridstep\sseq@xminpadding{x}{\sseq@leftgridpadding}{1/2}
+ \sseq@grid@atmostgridstep\sseq@xmaxpadding{x}{\sseq@rightgridpadding}{1/2}
+ \sseq@grid@atmostgridstep\sseq@yminpadding{y}{\sseq@bottomgridpadding}{1/2}
+ \sseq@grid@atmostgridstep\sseq@ymaxpadding{y}{\sseq@topgridpadding}{1/2}
+%
+ \pgftransformshift{\pgfpointxy{-\sseq@xgridstep/2}{-\sseq@ygridstep/2}}
+ \pgfpathgrid[stepx=\pgf@xb,stepy=\pgf@yb]
+ { \pgfpointxy
+ { \sseq@xmin - \sseq@xminpadding + 0.01 + \sseq@xoffset + \sseq@xgridstep/2 }
+ { \sseq@ymin - \sseq@yminpadding + 0.01 + \sseq@yoffset + \sseq@ygridstep/2 } }
+ { \pgfpointxy
+ { \sseq@xmax + \sseq@xmaxpadding - 0.01 + \sseq@xoffset + \sseq@xgridstep/2 }
+ { \sseq@ymax + \sseq@ymaxpadding - 0.01 + \sseq@yoffset + \sseq@ygridstep/2 } }
+ \pgfusepath{stroke}
+ \endpgfscope
+ \egroup
+}
+\def\sseq@grid@go{
+ \bgroup
+ \pgfscope
+ \sseq@useclip
+ \pgfsetcolor{\sseq@gridcolor}
+ \pgfsetlinewidth{\the\sseq@gridstrokethickness}
+ \sseq@grid@setstepandgridstep
+%
+ % We don't want the grid to end with an extra line, so extend it by one step distance or
+ % \sseq@---gridpadding, whichever is shorter.
+ \sseq@grid@atmostgridstep\sseq@xminpadding{x}{\sseq@leftgridpadding}{1}
+ \sseq@grid@atmostgridstep\sseq@xmaxpadding{x}{\sseq@rightgridpadding}{1}
+ \sseq@grid@atmostgridstep\sseq@yminpadding{y}{\sseq@bottomgridpadding}{1}
+ \sseq@grid@atmostgridstep\sseq@ymaxpadding{y}{\sseq@topgridpadding}{1}
+%
+ \pgfpathgrid[stepx=\pgf@xb,stepy=\pgf@yb]
+ { \pgfpointxy
+ { \sseq@xmin - \sseq@xminpadding + 0.01 + \sseq@xoffset }
+ { \sseq@ymin - \sseq@yminpadding + 0.01 + \sseq@yoffset } }
+ { \pgfpointxy
+ { \sseq@xmax + \sseq@xmaxpadding - 0.01 + \sseq@xoffset }
+ { \sseq@ymax + \sseq@ymaxpadding - 0.01 + \sseq@yoffset }
+ }
+ \pgfusepath{stroke}
+ \endpgfscope
+ \egroup
+}
+\def\sseq@grid@none{}
+\def\sseq@grid@dots{
+ \bgroup
+ \pgfscope
+ \pgfgettransform\sseq@savetransform
+ \pgfgettransformentries{\sseq@a}{\sseq@b}{\sseq@c}{\sseq@d}{\sseq@u}{\sseq@v}
+ \pgfsettransform\sseq@savetransform
+ \sseq@useclip
+ \pgftransformshift{\pgfqpoint{-1.5cm}{-0.5cm}}
+ \pgfsetdash{{1pt}{\sseq@a*1cm-1pt}}{0.5cm+.5pt}
+ \pgfsetlinewidth{1pt}
+ \sseq@tempy=\sseq@ymin\relax
+ \advance\sseq@tempy\m@ne
+ \loop
+ \advance\sseq@tempy\@ne
+ \pgfpathmoveto{\pgfpointxy{\sseq@xmin + 0.5/\sseq@a}{\the\sseq@tempy}}
+ \pgfpathlineto{\pgfpointxy{\sseq@xmax + 1.01 }{\the\sseq@tempy}}
+ \ifnum\sseq@tempy<\sseq@ymax\repeat
+ %\pgfpathgrid[stepx=1cm,stepy=1cm]{\pgfpoint{-0.5cm}{-0.5cm}}{\pgfpoint{\xmax cm-0.5cm}{\ymax cm-0.5cm}}
+ \pgfusepath{stroke}
+ \endpgfscope
+ \egroup
+}
+%%%
+%%% Draw Classes
+%%%
+%%% Class offsets
+\SseqNewClassPattern{standard}{
+ (0,0);
+ (-0.13,0)(0.13,0);
+ (-0.2,0)(0,0)(0.2,0);
+ (-0.13,-0.13)(0.13,-0.13)(-0.13,0.13)(0.13,0.13);
+ (-0.16,-0.16)(0.16,-0.16)(-0.16,0.16)(0.16,0.16)(0,0);
+ (-0.13,-0.2)(-0.13,0)(-0.13,0.2)(0.13,-0.2)(0.13,0)(0.13,0.2);
+}
+
+\sseqnewclasspattern{linear}{
+ (0,0);
+ (-0.13,0)(0.13,0);
+ (-0.2,0)(0,0)(0.2,0);
+ (-0.3,0)(-0.1,0)(0.1,0)(0.3,0);
+ (-0.4,0)(-0.2,0)(0,0)(0.2,0)(0.4,0);
+ (-0.5,0)(-0.3,0)(-0.1,0)(0.1,0)(0.3,0)(0.5,0);
+}
+\def\sseq@offset#1#2{
+ \sseq@eval{\@nx\pgftransformshift{
+ \@nx\pgfqpointxy
+ { \csname sseq@\sseq@classpattern xoffset#1/#2\endcsname }
+ { \csname sseq@\sseq@classpattern yoffset#1/#2\endcsname }
+ }}
+}
+
+\def\sseq@tooltip@wrapper#1#2{%
+ \edef\temp{\detokenize\@xpthree{#2}}%
+ \edef\temp{\@xp\sseqtooltip@replaceslashes\@xp{\temp}}%
+ \sseq@eval{\@nx\pdftooltip{\unexpanded{#1}}{\temp}}%
+}
+\bgroup\lccode`\!=`\\\lowercase{\egroup
+\def\sseqtooltip@replaceslashes#1{\sseqtooltip@replaceslashes@#1!\sseq@nil}
+\def\sseqtooltip@replaceslashes@#1!#2{%
+ #1%
+ \ifx\sseq@nil#2\@xp\@gobble\else
+ \@nx\@nx\@nx\textbackslash
+ \@xp\sseqtooltip@replaceslashes@
+ \fi#2%
+}
+}
+
+
+% #1 -- "class."
+% (#2,#3,#4) -- the class name x,y,n
+% #5 -- generation
+\def\sseq@class@getparts#1(#2,#3,#4)[#5].{
+ \sseq@seterrorannotation@drawing{#1}{#2}{#3}{#4}{#5}
+ \def\sseq@thisnodename{sseq{#2,#3,#4}}
+ \def\sseq@thispos{(#2,#3)}
+ \edef\sseq@thisposnodeindex{\sseq@obj{class.(#2,#3,#4).n}}
+ \edef\sseq@thispostotalnodes{\sseq@obj{partcoord.(#2,#3).numnodes}}
+ \def\sseq@thisclassnum{#5}
+ \sseq@tempx=#2\relax
+ \sseq@tempy=#3\relax
+}
+
+
+% #1 -- the name of the class object given as (x,y,n)[gen]
+% Mandatory fields:
+% partcoord.(x,y).numnodes
+% (x,y,n).num -- number of generations of this class
+% (x,y,n).n -- node n of numnodes (needed to allow node position to differ from creation order).
+% class.page -- the death page of the class
+% Optional fields:
+% class.options -- Options for this class.
+% class.nodetext -- The internal text of the node
+% class.nodetext.options -- options (color, font) to change the nodetext
+% class.labelnodes -- a list of external labels
+% class.name & class.showname -- If there is a name, use it as a label in some way
+% class.tooltip -- Make a tooltip. Fails if the user didn't load package with tooltip option
+% class.needstikz -- Use tikz to draw this node
+% Someday I should document this horrible mess of code here
+\newif\ifsseq@permanentcycle
+\newcount\sseq@totalclassesdrawn
+\AtEndDocument{\message{Total classes: \the\sseq@totalclassesdrawn}}
+\def\sseq@class@drawnode#1{%
+ \global\advance\sseq@totalclassesdrawn\@ne
+ \begingroup
+ \sseq@class@getparts#1.
+ % defines:
+ % \sseq@thisnodename -- name we should give the pdfnode
+ % \sseq@thispos -- (x,y)
+ % \sseq@thispostotalnodes -- total number of nodes at (x,y)
+ % \sseq@thisposnodeindex -- index of this node
+ \sseq@needstikzfalse
+ % Apply first pass styles? TODO: What is this for again?
+ \sseq@options@firstpassmode
+ \sseq@thesseqstyle
+ \sseq@theclassstyle
+ \ifnum\sseq@obj{#1.page}=\sseq@infinitycount
+ \sseq@thepermanentcyclestyle
+ \else
+ \sseq@thetransientcyclestyle
+ \ifsseq@thispage
+ \sseq@thethispagecyclestyle
+ \fi
+ \fi
+ \the\sseq@scope@toks
+ \sseq@obj{#1.needstikz}
+%
+ \sseq@outofrangetrue\relax % Mysterious that we need this \relax here...
+ % If we are in range, we'll draw the node, if not we just mark the coordinate.
+ \ifnum\sseq@tempx<\sseq@xmaxpp\relax\ifnum\sseq@tempx>\sseq@xminmm\relax\ifnum\sseq@tempy<\sseq@ymaxpp\relax\ifnum\sseq@tempy>\sseq@yminmm\relax
+ \sseq@outofrangefalse
+ \pgfscope
+ % Finish options setup
+ \let\tikz@options\pgfutil@empty
+ \let\tikz@alias=\pgfutil@empty
+ \def\pgfkeysdefaultpath{/sseqpages/class/}%
+ \sseq@options@secondpassmode
+ \sseq@thesseqstyle
+ \sseq@theclassstyle
+ \ifnum\sseq@obj{#1.page}=\sseq@infinitycount
+ \sseq@permanentcycletrue % This is to communicate with family style code...
+ \sseq@thepermanentcyclestyle
+ \else
+ \sseq@permanentcyclefalse
+ \sseq@thetransientcyclestyle
+ \ifsseq@thispage
+ \sseq@thethispagecyclestyle
+ \fi
+ \fi
+ \def\sseq@collections@featuretype{class}
+ \the\sseq@scope@toks
+ \sseq@obj{#1.options}
+ % Set up node position coordinate transform
+ \pgftransformshift{\pgfqpointxy{\numexpr\sseq@tempx +\sseq@xoffset-\sseq@x\relax}{\numexpr\sseq@tempy + \sseq@yoffset-\sseq@y\relax}}
+ \iftikz@fullytransformed\pgfgettransform{\savetransform}\fi
+ \pgftransformresetnontranslations
+ \sseq@globalrotatetransform
+ % Now the origin is at (x,y). Set up class placement offset.
+ \sseq@classplacementtransform
+ \sseq@obj@ifdef{#1.offset}{\sseq@obj{#1.offset}}{%
+ \sseq@offset{\sseq@thisposnodeindex}{\sseq@thispostotalnodes}%
+ }%
+ \iftikz@fullytransformed\pgfsettransform{\savetransform}\else\pgftransformresetnontranslations\ifsseq@rotatelabels\sseq@globalrotatetransform\fi\fi
+ %
+ \tikz@options
+ % the value of \sseq@class@showname comes from styles. If there was a local option with showname, it's stored in #1.showname.
+ % local value takes priority.
+ \let\sseq@classlabelnodes\pgfutil@empty
+ \edef\sseq@classnodetextoptions{\sseq@obj@ifdef{#1.nodetext.options}{\@xptwo\@nx\sseq@obj{#1.nodetext.options}}{}}
+ \edef\sseq@classnodetext{\sseq@obj@ifdef{#1.nodetext}{\@xptwo\@nx\sseq@obj{#1.nodetext}}{}}
+ \sseq@obj@ifdef{#1.showname}{\sseq@lettoobj\sseq@class@showname{#1.showname}}{}
+ \ifcsname sseq@class@showname\endcsname
+ \sseq@obj@ifdef{#1.name}{
+ \sseq@eval{\@nx\sseq@handleclassquotes@inner{\sseq@obj{#1.name}}{\sseq@class@showname}}
+ }{}
+ \fi
+ % Okay now we're ready to make the node
+ \ifsseq@needstikz
+ % The options code above put mode information into \tikz@mode which gets wiped by tikz
+ \let\sseq@mode\tikz@mode
+ \sseq@eval{%
+ \@nx\node[%
+ every text node part/.code/.expand once={\sseq@globalrotatetransform\sseq@classnodetextoptions{}},%
+ /utils/exec={\let\@nx\tikz@mode\@nx\sseq@mode}] % Set mode based on outer options
+ (\sseq@thisnodename) {\unexpanded\@xp{\sseq@classnodetext}};%
+ }%
+ \else
+ \tikz@node@textfont
+ \sseq@setnodetext{\sseq@classnodetext}{\sseq@classnodetextoptions}%
+ \let\tikz@fig@name\sseq@thisnodename
+ \pgfmultipartnode{\tikz@shape}{\tikz@anchor}{\tikz@fig@name}{\sseq@drawnode}%
+ \tikz@alias % makes extra names for this shape
+ \fi
+ \sseq@obj{#1.labelnodes}%
+ \sseq@classlabelnodes % classlabelnodes comes from show name (I guess it can't put them into #1.labelnodes? TODO: why?)
+ \sseq@obj@ifdef{#1.tooltip}{\sseq@dotooltip{#1}}{}%
+ \endpgfscope
+ \fi\fi\fi\fi
+ % If the node is out of range, to save time we don't draw anything. However, structlines etc may depend on the out of range node,
+ % so we mark the coordinate.
+ \ifsseq@outofrange
+ \sseq@eval{\@nx\pgftransformshift{\@nx\pgfqpointxy{\numexpr\sseq@tempx+\sseq@xoffset-\sseq@x\relax}{\numexpr\sseq@tempy+\sseq@yoffset-\sseq@y\relax}}}%
+ \pgftransformresetnontranslations
+ \sseq@globalrotatetransform
+ \sseq@classplacementtransform
+ \sseq@offset{\sseq@thisposnodeindex}{\sseq@thispostotalnodes}%
+ \pgfcoordinate{\sseq@thisnodename}{\pgfpointorigin}%
+ \fi
+ \endgroup
+}
+
+\def\sseq@dotooltip#1{%
+ \pgfpointanchor{\sseq@thisnodename}{west}%
+ \pgf@xa=\pgf@x
+ \pgfpointanchor{\sseq@thisnodename}{south}%
+ \pgf@ya=\pgf@y
+%
+ \pgf@process{\pgfpointdiff{\pgfpointtransformed{\pgfpointanchor{\sseq@thisnodename}{west}}}{\pgfpointtransformed{\pgfpointanchor{\sseq@thisnodename}{east}}}}%
+ \pgf@xb=\pgf@x
+ \pgf@process{\pgfpointdiff{\pgfpointtransformed{\pgfpointanchor{\sseq@thisnodename}{south}}}{\pgfpointtransformed{\pgfpointanchor{\sseq@thisnodename}{north}}}}%
+ \pgf@yb=\pgf@y
+%
+ \setbox\tikz@tempbox=\hbox{%
+ \pgfinterruptpicture
+ \sseqtooltip{\rule{\pgf@xb}{0pt}\rule{0pt}{\pgf@yb}}{\sseq@obj{#1.tooltip}}%
+ \endpgfinterruptpicture
+ }
+ {%
+ \pgftransformshift{\pgfqpoint{\pgf@xa}{\pgf@ya}}%
+ \pgfapproximatenonlineartransformation%
+ \pgfqboxsynced{\tikz@tempbox}%
+ }%
+}
+
+% #1 -- label text
+% #2 -- options
+% Make the node textbox.
+\def\sseq@setnodetext#1#2{%
+ \sseq@ifempty{#1}{% more often than not, the node is empty...
+ \setbox\pgfnodeparttextbox=\hbox{}%
+ }{%
+ \setbox\pgfnodeparttextbox=\hbox{%
+ \pgfscope%
+ #2
+ \tikzset{every text node part/.try}%
+ \ifx\tikz@textopacity\pgfutil@empty%
+ \else%
+ \pgfsetfillopacity{\tikz@textopacity}%
+ \pgfsetstrokeopacity{\tikz@textopacity}%
+ \fi%
+ \pgfinterruptpicture
+ \ifx\tikz@text@width\pgfutil@empty%
+ \tikz@textfont%
+ \else%
+ \begingroup%
+ \pgfmathsetlength{\pgf@x}{\tikz@text@width}%
+ \pgfutil@minipage[t]{\pgf@x}\leavevmode\hbox{}%
+ \tikz@textfont%
+ \tikz@text@action%
+ \fi%
+ \ifx\tikz@textcolor\pgfutil@empty%
+ \else%
+ \pgfutil@colorlet{.}{\tikz@textcolor}%
+ \fi%
+ \pgfsetcolor{.}%
+ \tikz@atbegin@node%
+ #1%
+ \tikz@atend@node%
+ \ifx\tikz@text@width\pgfutil@empty%
+ \else%
+ \pgfutil@endminipage%
+ \endgroup%
+ \fi%
+ \endpgfinterruptpicture
+ \endpgfscope%
+ }%
+ \ifx\tikz@text@width\pgfutil@empty%
+ \else%
+ \pgfmathsetlength{\pgf@x}{\tikz@text@width}%
+ \wd\pgfnodeparttextbox=\pgf@x%
+ \fi%
+ \ifx\tikz@text@height\pgfutil@empty%
+ \else%
+ \pgfmathsetlength{\pgf@x}{\tikz@text@height}%
+ \ht\pgfnodeparttextbox=\pgf@x%
+ \fi%
+ \ifx\tikz@text@depth\pgfutil@empty%
+ \else%
+ \pgfmathsetlength{\pgf@x}{\tikz@text@depth}%
+ \dp\pgfnodeparttextbox=\pgf@x%
+ \fi%
+ }
+}
+
+% A small part of the tikz main loop that has been paired down as much as possible for efficiency.
+\def\sseq@drawnode{%
+ \pgfutil@tempdima=\pgflinewidth%
+ {%
+ \tikz@mode%
+ \iftikz@mode@draw%
+ \iftikz@mode@double%
+ % Change line width
+ \begingroup%
+ \pgfsys@beginscope%
+ \tikz@double@setup%
+ \fi%
+ \fi%
+ %
+ % Step 10: Do stroke/fill as needed
+ %
+ \sseq@eval{\noexpand\pgfusepath{%
+ \iftikz@mode@fill fill,\fi%
+ \iftikz@mode@draw draw,\fi%
+ }}%
+ %
+ % Step 11: Double stroke, if necessary
+ %
+ \iftikz@mode@draw%
+ \iftikz@mode@double%
+ \pgfsys@endscope%
+ \endgroup%
+ \fi%
+ \fi
+ }%
+ \global\pgflinewidth=\pgfutil@tempdima%
+}
+
+%%% Labels
+
+% #1 -- label text
+% #2 -- options
+\def\sseq@drawlabel#1#2{%
+ \bgroup\pgfscope
+ \def\tikz@mode{}%
+ \let\sseq@tikz@transform@save\tikz@transform
+ \pgfkeyssetvalue{/pgf/inner xsep}{2pt}%
+ \pgfkeyssetvalue{/pgf/inner ysep}{2pt}%
+ \def\tikz@shape{rectangle}
+ \let\tikz@transform\empty % The next line was set up to fix the classlabelstyle glitch (what does this mean?)
+ \sseq@options@secondpassmode
+ \sseq@thesseqstyle\sseq@thelabelstyle\sseq@theclasslabelstyle#2
+ \tikz@options
+ \pgftransformreset
+ \pgftransformshift{\tikz@node@at}%
+ \tikz@lib@pos@call
+ \tikz@transform
+ \tikz@mode
+ \let\tikz@transform\sseq@tikz@transform@save
+ \sseq@setnodetext{\sseq@labeltextfn{#1}}{}%
+ \pgfmultipartnode{\tikz@shape}{\tikz@anchor}{label}{\sseq@drawnode}%
+ \ifsseq@pin
+ \def\sseq@pinoptions{}%
+ \let\tikz@options\empty
+ \let\tikz@mode\empty
+ \sseq@thepinstyle
+ #2
+ \sseq@pinoptions
+ \tikz@options
+ \tikz@mode
+ \sseq@drawedge@findsourcetarget{\tikz@fig@name}{}{label}{}%
+ \pgfpathmoveto{\sseq@sourcecoord}%
+ \pgfpathlineto{\sseq@targetcoord}%
+ \sseq@eval{\noexpand\pgfusepath{%
+ draw,
+ \iftikz@mode@fill fill,\fi
+ \iftikz@mode@draw draw,\fi
+ }}%
+ \fi
+ \endpgfscope\egroup
+}
+
+
+%%%
+%%% Drawing edges
+%%%
+\def\sseq@ifinrange(#1){\sseq@ifinrange@#1,\sseq@nil}
+\def\sseq@ifinrange@#1,#2,#3\sseq@nil{%
+ \sseq@tempx=#1\relax\sseq@tempy=#2\relax
+ \sseq@outofrangetrue
+ \ifnum\sseq@tempx<\sseq@xmaxpp\relax\ifnum\sseq@tempx>\sseq@xminmm\relax\ifnum\sseq@tempy<\sseq@ymaxpp\relax\ifnum\sseq@tempy>\sseq@yminmm\relax
+ \sseq@outofrangefalse
+ \fi\fi\fi\fi
+ \ifsseq@outofrange
+ \@xp\pgfutil@secondoftwo
+ \else
+ \@xp\pgfutil@firstoftwo
+ \fi
+}
+
+% #1 -- source node
+% #2 -- source anchor
+% #3 -- target node
+% #4 -- target anchor
+% Calculate actual start and end of the edge (node borders), return the results stored in \sseq@sourcecoord, \sseq@targetcoord
+\def\sseq@drawedge@findsourcetarget#1#2#3#4{
+ \edef\sseq@edgesourceanchor{#2}
+ \edef\sseq@edgetargetanchor{#4}
+ \let\tempaf\pgfutil@empty
+ \ifx\sseq@edgesourceanchor\pgfutil@empty % Check that the source doesn't have a specified anchor
+ \def\tempa{\pgfpointanchor{#1}{center}}% if so, start by taking the center of that coordinate
+ \else
+ \edef\tempa{\@nx\pgfpointanchor{#1}{\sseq@edgesourceanchor}} % If it has an anchor, use that
+ \let\tempaf\tempa
+ \fi
+ \ifx\sseq@edgetargetanchor\pgfutil@empty % check that the target doesn't have a specified anchor
+ \def\tempb{\pgfpointshapeborder{#3}{\tempa}}% if so, our end point is the point on the boundary of node b that is in the direction of our initial start coordinate
+ \else
+ \edef\tempb{\@nx\pgfpointanchor{#3}{\sseq@edgetargetanchor}}% If it has a specified anchor, use that
+ \fi
+ \let\tempbf\tempb
+ \ifx\tempaf\pgfutil@empty%
+ \def\tempaf{\pgfpointshapeborder{#1}{\tempb}}%
+ \fi
+ \let\sseq@sourcecoord\tempaf
+ \let\sseq@targetcoord\tempbf
+}
+
+\def\sseq@fullcoord@to@partialcoord(#1){\sseq@fullcoord@to@partialcoord@#1,\@nil}
+\def\sseq@fullcoord@to@partialcoord@#1,#2,#3\@nil{{#1cm}{#2cm}}
+% #1 -- source (full)
+% #2 -- target (full)
+% #3 -- which type of edge (either "structline" or "differential")
+% #4 -- options
+\def\sseq@drawedge(#1)(#2)#3#4{%
+ \begingroup\pgfscope
+ % If either class is part of a family we aren't drawing, don't draw the edge either.
+ \expandafter\ifx\csname pgf@sh@pi@sseq{#1}\endcsname\pgfpictureid\else
+ \@xp\sseq@break
+ \fi
+ \expandafter\ifx\csname pgf@sh@pi@sseq{#2}\endcsname\pgfpictureid\else
+ \@xp\sseq@break
+ \fi
+ \def\sseq@edgetype{#3}
+ \let\sseq@collections@featuretype\sseq@edgetype
+ \let\sseq@edgesourceanchor\pgfutil@empty
+ \let\sseq@edgetargetanchor\pgfutil@empty
+%
+ \sseq@needstikzfalse
+ \def\pgfkeysdefaultpath{/sseqpages/#3/}%
+ \sseq@options@bothpassmode
+ \sseq@thesseqstyle
+ \sseq@theedgestyle\csname sseq@the#3style\endcsname
+ \ifsseq@thispage
+ \csname sseq@thethispage#3style\endcsname
+ \fi
+ \the\sseq@scope@toks
+ #4%
+ \csname sseq@collections@#3@hook\endcsname
+ \pgftransformshift{\pgfqpointxy{-\the\sseq@x}{-\the\sseq@y}}%
+ % puts results into \sseq@sourcecoord and \sseq@targetcoord
+ \sseq@drawedge@findsourcetarget{sseq{#1}}{\sseq@edgesourceanchor}{sseq{#2}}{\sseq@edgetargetanchor}
+%
+ \tikz@options
+ \tikz@mode
+ \def\temparrowstartspec{}%
+ \def\temparrowendspec{}%
+ \pgfcoordinate{tempa}{\sseq@sourcecoord}%
+ \pgfcoordinate{tempb}{\sseq@targetcoord}%
+ \pgftransformreset
+ \sseq@outofrangefalse
+ \sseq@ifinrange(#1){}{
+ \edef\temparrowstartspec{\@nx\pgfsetarrowsstart{\csname sseq@runoffarrow@start@#3@spec\endcsname}}
+ \sseq@outofrangetrue
+ }%
+ \sseq@ifinrange(#2){}{
+ \edef\temparrowendspec{\@nx\pgfsetarrowsend{\csname sseq@runoffarrow@end@#3@spec\endcsname}}
+ \sseq@outofrangetrue
+ }
+ \ifsseq@outofrange
+ \sseq@handleoffpageedge{#1}{#2}%
+ \fi
+ \ifsseq@drawedge
+ % TODO: should some sort of transformation manipulation be here? Maybe allow user to specify preference?
+ % Don't draw dots on very short segments
+ \pgfpointdiff{\sseq@targetcoord}{\sseq@sourcecoord}
+ \pgfmathveclen{\pgf@x}{\pgf@y}%
+ \@xp\pgfmathint\@xp{\pgfmathresult}%
+ \ifnum\pgfmathresult<10\relax%%17? % TODO: Fix this predicate
+ \tikzset{every text node part/.append code={\pgfsetcolor{white}}}% I wonder why this is here...
+ \ifx\temparrowstartspec\pgfutil@empty
+ \else
+ \def\temparrowstartspec{\pgfsetarrowsstart{}}%
+ \fi
+ \ifx\temparrowendspec\pgfutil@empty
+ \else
+ \def\temparrowendspec{\pgfsetarrowsend{}}%
+ \fi
+ \fi
+ \ifsseq@needstikz
+ \draw[/sseqpages,%
+ /utils/exec={%
+ \sseq@thesseqstyle\sseq@theedgestyle
+ \csname sseq@the#3style\endcsname
+ \ifsseq@thispage
+ \csname sseq@thethispage#3style\endcsname
+ \fi
+ \the\sseq@scope@toks
+ \temparrowstartspec\temparrowendspec #4%
+ }%
+ ] (tempa) to (tempb);%
+ \else
+ \temparrowstartspec
+ \temparrowendspec
+ \pgfpathmoveto{\pgfpointanchor{tempa}{center}}%
+ \pgfpathlineto{\pgfpointanchor{tempb}{center}}%
+ \sseq@eval{\noexpand\pgfusepath{%
+ \iftikz@mode@fill fill,\fi
+ \iftikz@mode@draw draw,\fi
+ }}%
+ \fi
+ \fi
+ \sseq@breakpoint
+ \endpgfscope\endgroup
+}
+
+% TODO: this macro is super expensive. Make it faster
+\def\sseq@handleoffpageedge#1#2{
+ \pgfpathmoveto{\sseq@sourcecoord}%
+ \pgfpathlineto{\sseq@targetcoord}%
+ \pgfgetpath\thispath
+ \pgfusepath{discard}%
+ \pgfintersectionsortbysecondpath
+ \pgfintersectionofpaths{\pgfsetpath\sseq@theclippath}{\pgfsetpath\thispath}%
+ \ifcase\pgfintersectionsolutions\relax
+ % No intersections, but one or both endpoints may be out of range but still in clipping region due to scaling. Add ellipses as appropriate.
+ \sseq@ifinrange(#1){% If the first endpoint is in range, the second must be out of range b/c sseq@outofrange is true.
+ %\edef\temparrowendspec{\@nx\pgfsetarrowsend{\csname sseq@runoffarrow@end@#3@spec\endcsname}}
+ }{%
+ \sseq@ifinrange(#2){}{\sseq@drawedge@handletrickyedge}% uh-oh, both ends are out of range
+ }%
+ \or
+ \sseq@ifinrange(#1){% If the startpoint is in range, the intersection must be the end.
+ \def\sseq@targetcoord{\pgfpointintersectionsolution{1}}
+ \pgfcoordinate{tempb}{\sseq@targetcoord}
+ }{%
+ \sseq@ifinrange(#2){% If the startpoint is out of range and the endpoint is in range, the intersection must be the start
+ \def\sseq@sourcecoord{\pgfpointintersectionsolution{1}}%
+ \pgfcoordinate{tempa}{\sseq@sourcecoord}%
+ }{\sseq@drawedge@handletrickyedge}% Uh-oh, both ends are out of range.
+ }
+ \or% an orphan
+ \ifsseq@draworphanedges
+ \sseq@drawedge@handleorphan
+ \else
+ \sseq@drawedgefalse % Don't draw "orphaned edges"
+ \fi
+ \else
+ \sseq@error{clip-not-convex}%
+ \sseq@breakfifi
+ \fi
+}
+
+\def\sseq@drawedge@handletrickyedge{%
+ \ifsseq@draworphanedges
+ \pgfintersectionsortbysecondpath
+ \pgfintersectionofpaths{\pgfsetpath\sseq@therangepath}{\pgfsetpath\thispath}%
+ \ifnum\pgfintersectionsolutions=\z@
+ \sseq@drawedgefalse % don't draw orphan edges that never intersect actual range
+ \else% Now we have to make a line through tempa and tempb long enough so that it intersects the original clip area twice.
+ \pgfmathanglebetweenpoints{\pgfpointanchor{tempa}{center}}{\pgfpointanchor{tempb}{center}}%
+ \edef\tempangle{\pgfmathresult}
+ \pgfpathmoveto{\pgfpointadd{\pgfpointanchor{tempa}{center}}{\pgfpointpolar{\tempangle}{100cm}}}% a really long line
+ \pgfpathlineto{\pgfpointadd{\pgfpointanchor{tempa}{center}}{\pgfpointpolar{\tempangle}{-100cm}}}%
+ \pgfgetpath\thispath
+ \pgfusepath{discard}
+ \pgfintersectionofpaths{\pgfsetpath\sseq@theclippath}{\pgfsetpath\thispath}
+ \sseq@drawedge@handleorphan
+ \fi
+ \else
+ \sseq@drawedgefalse
+ \fi
+}
+
+
+\def\sseq@drawedge@handleorphan{%
+ \def\sseq@sourcecoord{\pgfpointintersectionsolution{2}}%
+ \def\sseq@targetcoord{\pgfpointintersectionsolution{1}}%
+ \edef\temparrowstartspec{\@nx\pgfsetarrowsstart{\csname sseq@runoffarrow@start@\sseq@edgetype @spec\endcsname}}%
+ \edef\temparrowendspec{\@nx\pgfsetarrowsend{\csname sseq@runoffarrow@end@\sseq@edgetype @spec\endcsname}}%
+ \pgfcoordinate{tempa}{\sseq@sourcecoord}%
+ \pgfcoordinate{tempb}{\sseq@targetcoord}%
+}
+
+
+% #1 -- uid
+% #2 -- first coordinate
+% #3 -- second coordinate
+\def\sseq@circleclass@draw#1#2#3{
+ \begingroup
+ % If either class is part of a family we aren't drawing, don't draw the fit either.
+ \expandafter\ifx\csname pgf@sh@pi@sseq{#2}\endcsname\pgfpictureid\else
+ \@xp\sseq@break
+ \fi
+ \expandafter\ifx\csname pgf@sh@pi@sseq{#3}\endcsname\pgfpictureid\else
+ \@xp\sseq@break
+ \fi
+ \pgfmathanglebetweenpoints{\pgfpointanchor{sseq{#2}}{center}}{\pgfpointanchor{sseq{#3}}{center}}
+ \let\tempangle\pgfmathresult
+ \let\tikz@lib@fit@scan@handle\sseq@fit@tikz@lib@fit@scan@handle % install fit modifications.
+ \let\tikz@calc@anchor\sseq@fit@tikz@calc@anchor
+ \sseq@tempiftrue
+ \sseq@options@secondpassmode
+ \node[
+ rotate fit=\tempangle,
+ /utils/exec={\sseq@thesseqstyle\sseq@thecircleclassstyle\the\sseq@scope@toks\sseq@savedoptioncode
+ \sseq@obj{#1.options}
+ \sseq@obj{#1.fitnodes}
+ \@xp\pgfkeysalso\@xp{\romannumeral0\sseq@obj{#1.tikzprimoptions}}
+ }
+ ] {};
+ \sseq@breakpoint
+ \endgroup
+}
+% Modifies tikz commands \tikz@lib@fit@scan@handle from \pgf\frontendlayer\tikz\libraries\tikzlibraryfit.code.tex line 81 and
+% \tikz@calc@anchor from \pgf\frontendlayer\tikz\tikz.code.tex line 5164
+% make it so that fit silently ignores nodes that are not defined.
+% This is copied with modification from \pgf\frontendlayer\tikz\libraries\tikzlibraryfit.code.tex line 81
+\def\sseq@fit@tikz@lib@fit@scan@handle#1{%
+ \ifsseq@tempif % this has been set in the following macro to be true if there is a node with the given name. If it's not true, ignore this.
+ \iftikz@shapeborder%
+ % Ok, fit all four external anchors, if they exist
+ \tikz@lib@fit@adjust{\pgfpointanchor{\tikz@shapeborder@name}{west}}%
+ \tikz@lib@fit@adjust{\pgfpointanchor{\tikz@shapeborder@name}{east}}%
+ \tikz@lib@fit@adjust{\pgfpointanchor{\tikz@shapeborder@name}{north}}%
+ \tikz@lib@fit@adjust{\pgfpointanchor{\tikz@shapeborder@name}{south}}%
+ \else%
+ \tikz@lib@fit@adjust{#1}%
+ \fi%
+ \fi
+ \sseq@tempiftrue
+ \tikz@lib@fit@scan%
+}
+
+%\def\tikz@calc@anchor#1.#2\tikz@stop{%
+% \ifcsname pgf@sh@ns@\tikz@pp@name {#1}\endcsname
+% \pgfpointanchor {\tikz@pp@name {#1}}{#2}%
+% \else
+% \pgfpointanchor {#1}{#2}%
+% \fi
+%}
+%l.8 \show\tikz@calc@anchor
+
+% This is copied with modification from \pgf\frontendlayer\tikz\tikz.code.tex line 5164
+\def\sseq@fit@tikz@calc@anchor#1.#2\tikz@stop{%
+ \ifcsname pgf@sh@ns@\tikz@pp@name {#1}\endcsname
+ \pgfpointanchor {\tikz@pp@name {#1}}{#2}%
+ \else
+ \ifcsname pgf@sh@ns@#1\endcsname
+ \pgfpointanchor {#1}{#2}%
+ \else
+ \sseq@tempiffalse
+ \fi
+ \fi
+}
+%\def\sseq@fit@tikz@calc@anchor#1.#2\tikz@stop{%
+% \pgfutil@ifundefined{pgf@sh@ns@#1}{\sseq@tempiffalse}{%If the node doesn't exist, don't throw an error but record that we should skip it
+% \pgfpointanchor{\tikz@pp@name{#1}}{#2}%
+% }%s
+%}
+
+
+
+%%
+%% Patch tikz coords
+%%
+
+\def\sseq@patchtikzcoords{
+ \let\sseq@tikz@scan@one@point@noshift\sseq@tikz@scan@one@point@noshift@active
+ \tikzoption{shift}{\sseq@tikzshift{##1}}
+ \let\tikz@@@parse@regular\sseq@tikz@@@parse@regular
+ \let\tikz@to@curve@path\sseq@tikz@to@curve@path
+ \let\tikz@@@to@compute@relative\sseq@tikz@@@to@compute@relative
+
+ \let\tikz@grid\sseq@tikz@grid
+ \let\tikz@scan@handle@options\sseq@tikz@scan@handle@options
+ \let\tikz@@@parse@polar\sseq@tikz@@@parse@polar
+ \let\tikz@scan@relative\sseq@tikz@scan@relative
+}
+\newif\ifsseq@draw@addoffset % control whether to offset coordinates by (\sseq@xoffset,\sseq@yoffset).
+\sseq@draw@addoffsettrue
+
+% Some of the stuff in tikzlibrarycalc will probably be broken, hopefully not too much
+\let\sseq@tikz@scan@one@point@noshift\tikz@scan@one@point
+\let\sseq@tikz@@@parse@regular@save\tikz@@@parse@regular
+
+\def\sseq@tikz@scan@relative#1+{%
+ \global\sseq@draw@addoffsetfalse
+ %\global\let\tikz@@@parse@regular\sseq@tikz@@@parse@regular@save
+ \pgfutil@ifnextchar+{%
+ \tikz@scan@plusplus{\global\sseq@draw@addoffsettrue#1}%
+ }{%
+ \tikz@scan@oneplus{\global\sseq@draw@addoffsettrue#1}%
+ }%
+}
+
+\def\sseq@tikz@scan@one@point@noshift@active#1{%
+ \global\sseq@draw@addoffsetfalse
+ \def\sseq@scanonepoint@cmd{\global\sseq@draw@addoffsettrue#1}
+ \tikz@scan@one@point\sseq@scanonepoint@cmd%
+}
+
+% Probably there are more places that shouldn't have shifts inserted.
+\def\sseq@tikzshift#1{\tikz@addtransform{\sseq@tikz@scan@one@point@noshift\pgftransformshift#1\relax}}
+
+
+\def\sseq@tikz@to@curve@path{%
+ [every curve to]
+ \pgfextra{
+ %\sseq@draw@addoffsetfalse
+ \let\tikz@@@parse@regular\sseq@tikz@@@parse@regular@save % I added this to prevent repeated offsets from screwing us up
+ \let\sseq@tikz@scan@one@point@noshift\tikz@scan@one@point
+ \iftikz@to@relative\tikz@to@compute@relative\else\tikz@to@compute\fi
+ }
+ \tikz@computed@path
+ \tikztonodes%
+}
+
+\let\sseq@tikz@@@to@compute@relative\tikz@@@to@compute@relative
+
+
+\patchcmd\sseq@tikz@@@to@compute@relative{%
+ \let\tikz@second@point=\tikz@toto
+}{%
+ \pgf@process{\pgfpointadd{\tikz@toto}{\pgfqpointxy{\sseq@xoffset}{\sseq@yoffset}}}%
+ \edef\tikz@toto{\@nx\pgfpoint{\the\pgf@x}{\the\pgf@y}}%
+ \let\tikz@second@point=\tikz@toto
+}{}{\error}
+
+
+
+
+% \tikz@parse@splitxyz: we should set up an error to make this unreachable?
+\let\sseq@tikz@grid\tikz@grid % line 3158
+\let\sseq@tikz@scan@handle@options\tikz@scan@handle@options % 4959
+\let\sseq@tikz@@@parse@polar\tikz@@@parse@polar % 5063
+
+\def\sseq@tikz@@@parse@regular#1#2#3){%
+ \pgfutil@in@,{#3}%
+ \ifpgfutil@in@%
+ \tikz@parse@splitxyz{#1}{#2}#3,% Perhaps put an error here? We probably don't handle 3d coords correctly.
+ \else%
+ \tikz@checkunit{#2}%
+ \iftikz@isdimension%
+ \def\temp@xcoord{(#2)/1cm}%
+ \else
+ \def\temp@xcoord{#2}%
+ \fi
+ \tikz@checkunit{#3}%
+ \iftikz@isdimension
+ \def\temp@ycoord{(#3)/1cm}%
+ \else
+ \def\temp@ycoord{#3}%
+ \fi
+ \ifsseq@draw@addoffset
+ \edef\temp@xcoord{\unexpanded\@xp{\temp@xcoord}+\unexpanded\@xp{\sseq@xoffset}}%
+ \edef\temp@ycoord{\unexpanded\@xp{\temp@ycoord}+\unexpanded\@xp{\sseq@yoffset}}%
+ \fi
+ \edef\@next{\unexpanded{#1}{\@nx\pgfpointxy{\unexpanded\@xp{\temp@xcoord}}{\unexpanded\@xp{\temp@ycoord}}}}%
+ \fi%
+ \@next%
+}
+
+
+
+
+%% New shapes and arrows
+%% These use lots of keys with spaces so it's convenient to turn off ExplSyntax.
+
+% Stolen from: https://tex.stackexchange.com/a/24621
+\pgfqkeys{/pgf}{
+ ellipse ratio/.code={\pgfkeyssetvalue{/pgf/ellipse ratio}{#1}},
+ ellipse ratio/.initial=1
+}
+\pgfdeclareshape{newellipse}
+{
+ \inheritsavedanchors[from=ellipse]
+ \inheritanchorborder[from=ellipse]
+ \savedanchor\radius{%
+ %
+ % Caculate ``height radius''
+ %
+ \pgf@y=.5\ht\pgfnodeparttextbox%
+ \advance\pgf@y by.5\dp\pgfnodeparttextbox%
+ \pgfmathsetlength\pgf@yb{\pgfkeysvalueof{/pgf/inner ysep}}%
+ \advance\pgf@y by\pgf@yb%
+ %
+ % Caculate ``width radius''
+ %
+ \pgf@x=.5\wd\pgfnodeparttextbox%
+ \pgfmathsetlength\pgf@xb{\pgfkeysvalueof{/pgf/inner xsep}}%
+ \advance\pgf@x by\pgf@xb%
+ %
+ % Adjust
+ %
+ \pgfkeysgetvalue{/pgf/ellipse ratio}{\ratioscale}
+ \pgfmathsetmacro\widthfactor{sqrt(\ratioscale^2+1)/\ratioscale}
+ \pgfmathsetmacro\heightfactor{sqrt(\ratioscale^2+1)}
+ \pgf@x=\widthfactor\pgf@x%
+ \pgf@y=\heightfactor\pgf@y%
+ %
+ % Adjust height, if necessary
+ %
+ \pgfmathsetlength\pgf@yc{\pgfkeysvalueof{/pgf/minimum height}}%
+ \ifdim\pgf@y<.5\pgf@yc%
+ \pgf@y=.5\pgf@yc%
+ \fi%
+ %
+ % Adjust width, if necessary
+ %
+ \pgfmathsetlength\pgf@xc{\pgfkeysvalueof{/pgf/minimum width}}%
+ \ifdim\pgf@x<.5\pgf@xc%
+ \pgf@x=.5\pgf@xc%
+ \fi%
+ %
+ % Add outer sep
+ %
+ \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}%
+ \pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}%
+ \advance\pgf@x by\pgf@xb%
+ \advance\pgf@y by\pgf@yb%
+ }
+
+ \inheritanchor[from=ellipse]{center}
+ \inheritanchor[from=ellipse]{mid}
+ \inheritanchor[from=ellipse]{base}
+ \inheritanchor[from=ellipse]{north}
+ \inheritanchor[from=ellipse]{south}
+ \inheritanchor[from=ellipse]{west}
+ \inheritanchor[from=ellipse]{mid west}
+ \inheritanchor[from=ellipse]{base west}
+ \inheritanchor[from=ellipse]{north west}
+ \inheritanchor[from=ellipse]{south west}
+ \inheritanchor[from=ellipse]{east}
+ \inheritanchor[from=ellipse]{mid east}
+ \inheritanchor[from=ellipse]{base east}
+ \inheritanchor[from=ellipse]{north east}
+ \inheritanchor[from=ellipse]{south east}
+
+ \inheritbackgroundpath[from=ellipse]
+}
+
+%%
+%%
+%% n concentric circles
+%%
+
+\tikzset{circlen/.code={\def\circlen@n{#1}\pgfkeysalso{shape=circlen@shape}}}
+\pgfdeclareshape{circlen@shape}
+{
+ \savedanchor\centerpoint{%
+ \pgf@x=.5\wd\pgfnodeparttextbox%
+ \pgf@y=.5\ht\pgfnodeparttextbox%
+ \advance\pgf@y by-.5\dp\pgfnodeparttextbox%
+ }
+
+ \saveddimen\radius{%
+ %
+ % Caculate ``height radius''
+ %
+ \pgf@ya=.5\ht\pgfnodeparttextbox%
+ \advance\pgf@ya by.5\dp\pgfnodeparttextbox%
+ \pgfmathsetlength\pgf@yb{\pgfkeysvalueof{/pgf/inner ysep}}%
+ \advance\pgf@ya by\pgf@yb%
+ %
+ % Caculate ``width radius''
+ %
+ \pgf@xa=.5\wd\pgfnodeparttextbox%
+ \pgfmathsetlength\pgf@xb{\pgfkeysvalueof{/pgf/inner xsep}}%
+ \advance\pgf@xa by\pgf@xb%
+ %
+ % Calculate length of radius vector:
+ %
+ \pgf@process{\pgfpointnormalised{\pgfqpoint{\pgf@xa}{\pgf@ya}}}%
+ \ifdim\pgf@x>\pgf@y%
+ \c@pgf@counta=\pgf@x%
+ \ifnum\c@pgf@counta=\z@%
+ \else%
+ \divide\c@pgf@counta by 255\relax%
+ \pgf@xa=16\pgf@xa\relax%
+ \divide\pgf@xa by\c@pgf@counta%
+ \pgf@xa=16\pgf@xa\relax%
+ \fi%
+ \else%
+ \c@pgf@counta=\pgf@y%
+ \ifnum\c@pgf@counta=\z@%
+ \else%
+ \divide\c@pgf@counta by 255\relax%
+ \pgf@ya=16\pgf@ya\relax%
+ \divide\pgf@ya by\c@pgf@counta%
+ \pgf@xa=16\pgf@ya\relax%
+ \fi%
+ \fi%
+ \pgf@x=\pgf@xa%
+ %
+ % If necessary, adjust radius so that the size requirements are
+ % met:
+ %
+ \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/minimum width}}%
+ \pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/minimum height}}%
+ \ifdim\pgf@x<.5\pgf@xb%
+ \pgf@x=.5\pgf@xb%
+ \fi%
+ \ifdim\pgf@x<.5\pgf@yb%
+ \pgf@x=.5\pgf@yb%
+ \fi%
+ %
+ % Now, add larger of outer sepearations.
+ %
+ \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}%
+ \pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}%
+ \ifdim\pgf@xb<\pgf@yb%
+ \advance\pgf@x by\pgf@yb%
+ \else%
+ \advance\pgf@x by\pgf@xb%
+ \fi%
+ \pgf@xb=2pt
+ \multiply\pgf@xb\circlen@n
+ \advance\pgf@x\pgf@xb
+ \advance\pgf@x-2pt\relax
+ }
+
+ %
+ % Anchors
+ %
+ \anchor{center}{\centerpoint}
+ \anchor{mid}{\centerpoint\pgfmathsetlength\pgf@y{.5ex}}
+ \anchor{base}{\centerpoint\pgf@y=0pt}
+ \anchor{north}{\centerpoint\advance\pgf@y by\radius}
+ \anchor{south}{\centerpoint\advance\pgf@y by-\radius}
+ \anchor{west}{\centerpoint\advance\pgf@x by-\radius}
+ \anchor{east}{\centerpoint\advance\pgf@x by\radius}
+ \anchor{mid west}{\centerpoint\advance\pgf@x by-\radius\pgfmathsetlength\pgf@y{.5ex}}
+ \anchor{mid east}{\centerpoint\advance\pgf@x by\radius\pgfmathsetlength\pgf@y{.5ex}}
+ \anchor{base west}{\centerpoint\advance\pgf@x by-\radius\pgf@y=0pt}
+ \anchor{base east}{\centerpoint\advance\pgf@x by\radius\pgf@y=0pt}
+ \anchor{north west}{
+ \centerpoint
+ \pgf@xa=\radius
+ \advance\pgf@x by-0.707107\pgf@xa
+ \advance\pgf@y by0.707107\pgf@xa
+ }
+ \anchor{south west}{
+ \centerpoint
+ \pgf@xa=\radius
+ \advance\pgf@x by-0.707107\pgf@xa
+ \advance\pgf@y by-0.707107\pgf@xa
+ }
+ \anchor{north east}{
+ \centerpoint
+ \pgf@xa=\radius
+ \advance\pgf@x by0.707107\pgf@xa
+ \advance\pgf@y by0.707107\pgf@xa
+ }
+ \anchor{south east}{
+ \centerpoint
+ \pgf@xa=\radius
+ \advance\pgf@x by0.707107\pgf@xa
+ \advance\pgf@y by-0.707107\pgf@xa
+ }
+ \anchorborder{
+ \pgf@xa=\pgf@x%
+ \pgf@ya=\pgf@y%
+ \edef\pgf@marshal{%
+ \noexpand\pgfpointborderellipse
+ {\noexpand\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}
+ {\noexpand\pgfqpoint{\radius}{\radius}}%
+ }%
+ \pgf@marshal%
+ \pgf@xa=\pgf@x%
+ \pgf@ya=\pgf@y%
+ \centerpoint%
+ \advance\pgf@x by\pgf@xa%
+ \advance\pgf@y by\pgf@ya%
+ }
+
+ %
+ % Background path
+ %
+ \behindbackgroundpath{
+ \pgfutil@tempdima=\radius%
+ \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}%
+ \pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}%
+ \ifdim\pgf@xb<\pgf@yb%
+ \advance\pgfutil@tempdima by-\pgf@yb%
+ \else%
+ \advance\pgfutil@tempdima by-\pgf@xb%
+ \fi%
+ \sseq@tempcount=\@ne
+ \loop
+ \pgfpathcircle{\centerpoint}{\pgfutil@tempdima}%
+ \advance\pgfutil@tempdima-2pt\relax
+ \advance\sseq@tempcount\@ne
+ \ifnum\sseq@tempcount<\circlen@n \repeat
+ \tikz@mode
+ \sseq@eval{\noexpand\pgfusepath{
+ \iftikz@mode@draw draw,\fi
+ }}
+ }
+ \backgroundpath{%
+ \pgfutil@tempdima=\radius%
+ \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}%
+ \pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}%
+ \ifdim\pgf@xb<\pgf@yb%
+ \advance\pgfutil@tempdima by-\pgf@yb%
+ \else%
+ \advance\pgfutil@tempdima by-\pgf@xb%
+ \fi%
+ \advance\pgfutil@tempdima2pt\relax
+ \pgfutil@tempdimb=-2pt\relax
+ \multiply\pgfutil@tempdimb\circlen@n
+ \advance\pgfutil@tempdima\pgfutil@tempdimb\relax
+ \pgfpathcircle{\centerpoint}{\pgfutil@tempdima}
+ }
+}
+
+
+
+% For out of bounds edges:
+
+\pgfdeclarearrow{
+ name = ...,
+ parameters = { \the\pgfarrowlength\the\pgflinewidth},
+ setup code = {
+ % The different end values:
+ \pgfarrowssetlineend{-\pgfarrowlength}
+ \pgfarrowssetbackend{-0.6\pgfarrowlength}
+ % The hull
+ \pgfarrowshullpoint{-\pgfarrowlength}{0pt}
+ \pgfarrowshullpoint{\pgfarrowlength}{0pt}
+ % Saves: Only the length:
+ \pgfarrowssavethe\pgfarrowlength
+ \pgfarrowssavethe\pgflinewidth
+ },
+ drawing code = {
+ \pgfpathcircle{\pgfpoint{-0.7\pgfarrowlength}{0pt}}{1.5\pgflinewidth}
+ \pgfpathcircle{\pgfpoint{-0.4\pgfarrowlength}{0pt}}{1.5\pgflinewidth}
+ \pgfpathcircle{\pgfpoint{-0.1\pgfarrowlength}{0pt}}{1.5\pgflinewidth}
+ \pgfpathclose
+ \pgfusepathqfill
+ },
+ defaults = { length = 0.3cm }
+}
+
diff --git a/graphics/pgf/contrib/spectralsequences/src/sseqforeach.code.tex b/graphics/pgf/contrib/spectralsequences/src/sseqforeach.code.tex
new file mode 100644
index 0000000000..ff849acd25
--- /dev/null
+++ b/graphics/pgf/contrib/spectralsequences/src/sseqforeach.code.tex
@@ -0,0 +1,266 @@
+%%
+%% Package: spectralsequences v1.3.0 2021-07-18
+%% Author: Hood Chatham
+%% Email: hood@mit.edu
+%% Date: 2021-07-18
+%% License: Latex Project Public License
+%%
+%% File: sseqforeach.code.tex
+%%
+%% Patches the \foreach command to obtain better error reporting
+%% Also defines some of my own looping commands, \Do, \DoUntilOutOfBounds, \DoUntilOutOfBoundsThenNMore
+%% All of the \foreach stuff we are modifying is defined by tikz in the file /pgf/utilities/pgffor.code.tex
+%%
+
+\def\sseq@for@vars{}
+\def\sseq@for@savemacro#1{\sseq@d@addto@macro\sseq@for@vars{\sseq@for@vars@do#1}}
+\def\sseq@for@savemacro@noslash#1{\sseq@d@addto@macro\sseq@for@vars{\sseq@for@vars@do@noslash#1}}
+\def\sseq@for@vars@do#1{; \string#1 = #1}
+\def\sseq@for@vars@do@noslash#1{; \sseq@macroname#1 = #1}
+
+\ExplSyntaxOn
+\def\sseq@for@printvars{\ifx\sseq@for@vars\pgfutil@empty\else\exp_last_unbraced:Nf\@gobble\sseq@for@vars\fi}
+
+\newcount\sseq@Do@depth
+
+\def\sseq@save@Do@var{
+ \sseq@atbeginforeach@msgsetup
+ \advance\sseq@Do@depth\@ne
+ \exp_args:Nc \sseq@for@savemacro@noslash { iteration ~ \the \sseq@Do@depth }
+ \def\iteration{0}
+}
+
+\def\sseq@stepiteration{
+ \edef\iteration{\the\numexpr\iteration+1}
+ \cs_set_eq:cN { iteration ~ \the \sseq@Do@depth } \iteration
+}
+
+\protected\def\sseq@Do{
+ \begingroup
+ \edef\sseq@inputline{\the\inputlineno}
+ \sseq@callas{\Do}
+ \sseq@call{\sseq@Do@}
+}
+
+\def\sseq@Do@#1#2{
+ \sseq@esetthiscall{\string\Do\unexpanded{#1}}
+ \sseq@opushstacktrace{\string\Do{#1}}
+ \sseq@ifintexpr{#1}{
+ \sseq@savestack
+ \sseq@save@Do@var
+ \prg_replicate:nn {#1} {
+ \sseq@stepiteration
+ #2
+ \relax
+ }
+ \sseq@restorestack
+ }{\sseq@error@xx{Do-invalid-int-expr}{\string\Do}{\unexpanded{#1}}}
+ \endgroup
+}
+
+\protected\def\sseq@DoUntilOutOfBounds{
+ \begingroup
+ \edef\sseq@inputline{\the\inputlineno}
+ \sseq@callas{\DoUntilOutOfBounds}
+ \sseq@call{\sseq@DoUntilOutOfBounds@}
+}
+
+\def\sseq@DoUntilOutOfBounds@ #1 {
+ \sseq@esetthiscall{\string\DoUntilOutOfBounds}
+ \sseq@opushstacktrace{\string\DoUntilOutOfBounds}
+ \ifx\sseq@xminmax\@gobbletwo\else\ifx\sseq@yminmax\@gobbletwo\else
+ \sseq@error@x{DoUntil-no-bound}{\string\DoUntilOutOfBounds}
+ \sseq@breakfifi
+ \fi\fi
+ \def\sseq@commandname{\string\DoUntilOutOfBounds}
+ \sseq@savestack % so we can nest these
+ \sseq@DoUntilOutOfBounds@body{#1}
+ \sseq@restorestack
+ \sseq@breakpoint
+ \endgroup
+}
+
+\protected\def\sseq@DoUntilOutOfBoundsThenNMore{
+ \begingroup
+ \edef\sseq@inputline{\the\inputlineno}
+ \sseq@callas{\DoUntilOutOfBoundsThenNMore}
+ \sseq@call{\sseq@DoUntilOutOfBoundsThenNMore@}
+}
+
+\def\sseq@DoUntilOutOfBoundsThenNMore@ #1#2 {
+ \sseq@esetthiscall{\string\DoUntilOutOfBoundsThenNMore\unexpanded{#1}}
+ \sseq@opushstacktrace{\string\DoUntilOutOfBoundsThenNMore{#1}}
+ \ifx\sseq@xminmax\@gobbletwo\else\ifx\sseq@yminmax\@gobbletwo\else
+ \sseq@error@n{DoUntil-no-bound}{\DoUntilOutOfBoundsThenNMore}
+ \sseq@breakfifi
+ \fi\fi
+ \sseq@ifintexpr{#1}{}{
+ \sseq@error@xx{Do-invalid-int-expr}{\string\DoUntilOutOfBoundsThenNMore}{\unexpanded{#1}}
+ \sseq@break
+ }
+ \def\sseq@commandname{\string\DoUntilOutOfBoundsThenNMore}
+ \sseq@savestack
+ \sseq@DoUntilOutOfBounds@body{#2}
+ \prg_replicate:nn {#1} {
+ \sseq@stepiteration
+ #2
+ \relax
+ }
+ \sseq@restorestack
+ \sseq@breakpoint
+ \endgroup
+}
+
+\def\sseq@DoUntilOutOfBounds@body#1{
+ \sseq@save@Do@var % Set iteration so that \DoUntilOutOfBoundsThenNMore doesn't get upset if we're already out of bounds
+ % If we're already out of bounds, we'll just do nothing.
+ \sseq_if_out_of_bounds_noparse:nnTF{\lastx{0}}{\lasty{0}}{
+ \bool_set_true:N \l_tmpa_bool
+ }{
+ \bool_set_false:N \l_tmpa_bool
+ % Now we need to set up the loop descent condition.
+ \sseq@tempxb=\lastx{0}\relax % Record current x and y values
+ \sseq@tempyb=\lasty{0}\relax
+ \sseq@stepiteration
+ #1 % run loop body once
+ \relax % protect from \d page grabber
+ \sseq_if_out_of_bounds_noparse:nnTF{\lastx{0}}{\lasty{0}}{ % If we're out of bounds now, we can quit
+ \bool_set_true:N \l_tmpa_bool
+ }{ % Otherwise, determine descent check
+ \sseq@tempx=\lastx{0}\relax % store new last value for comparison
+ \sseq@tempy=\lasty{0}\relax
+ \bool_set_false:N \l_tmpb_bool % This is to record whether there is a defined x range or y range. If neither, we'll throw an error.
+% \def\sseq@checkbound{\bool_set_false:N \l_tmpb_bool}
+ \ifx\sseq@xminmax\@gobbletwo
+ \ifnum\sseq@tempx>\sseq@tempxb
+ \bool_set_true:N \l_tmpb_bool % We have a descent condition
+% \sseq@d@addto@macro\sseq@checkbound{
+% \ifnum\sseq@tempx>\sseq@tempxb % Any stage of the loop is okay as long as it increases x
+% \bool_set_true:N \l_tmpb_bool
+% \fi
+% }
+ \else
+ \ifnum\sseq@tempx<\sseq@tempxb
+ \bool_set_true:N \l_tmpb_bool
+% \sseq@d@addto@macro\sseq@checkbound{ % Any stage of the loop is okay as long as it decreases x
+% \ifnum\sseq@tempx<\sseq@tempxb
+% \bool_set_true:N \l_tmpb_bool
+% \fi
+% }
+ \fi
+ \fi
+ \fi
+ \ifx\sseq@yminmax\@gobbletwo
+ \ifnum\sseq@tempy>\sseq@tempyb
+ \bool_set_true:N \l_tmpb_bool
+% \sseq@d@addto@macro\sseq@checkbound{
+% \ifnum\sseq@tempy>\sseq@tempyb
+% \bool_set_true:N \l_tmpb_bool
+% \fi
+% }
+ \else
+ \ifnum\sseq@tempy<\sseq@tempyb
+ \bool_set_true:N \l_tmpb_bool
+% \sseq@d@addto@macro\sseq@checkbound{
+% \ifnum\sseq@tempy<\sseq@tempyb
+% \bool_set_true:N \l_tmpb_bool
+% \fi
+% }
+ \fi
+ \fi
+ \fi
+ \if_bool:N \l_tmpb_bool\else:
+ \sseq@error@x{DoUntil-no-progress}{\sseq@commandname}
+ \fi:
+ }
+ }
+ \bool_until_do:Nn \l_tmpa_bool {
+ \sseq@stepiteration
+ #1
+ \relax
+ \sseq_if_out_of_bounds_noparse:nnTF{\lastx{0}}{\lasty{0}}{% we're done
+ \bool_set_true:N \l_tmpa_bool
+ }{% Check descent condition
+% \ifnum\iteration/10*10-\iteration=\z@
+% \sseq@tempx=\lastx{0}\relax % store new last value for comparison
+% \sseq@tempy=\lasty{0}\relax
+% \bool_set_false:N \l_tmpb_bool
+% \sseq@checkbound
+% \if_bool:N \l_tmpb_bool\else:
+% \sseq@error{DoUntilOutOfBounds-descent-failed}
+% \fi:
+% \sseq@tempxb=\sseq@tempx
+% \sseq@tempyb=\sseq@tempx
+% \fi
+ }
+ }
+}
+
+
+
+\ExplSyntaxOff
+
+
+\def\sseq@for@nopatch{\sseq@error{foreach-patch-failed}\def\sseq@patchfor{}\endinput}
+
+\newtoks\sseq@foreachcall
+
+\def\sseq@pgffor@atbeginforeach{%
+ \begingroup %
+ \sseq@atbeginforeach@msgsetup
+ % \pgffor@macro@list calls \pgffor@normal@list, so we need to mark that the list has already been added to foreachcall.
+ \sseq@tempiftrue
+}
+
+
+\def\sseq@patchfor{%
+ \let\pgffor@atbeginforeach\sseq@pgffor@atbeginforeach
+ \let\pgffor@@vars@opt\sseq@pgffor@@vars@opt
+}
+\def\sseq@pgffor@modify#1{%
+ \@xp\let\csname sseq@\sseq@macroname#1\endcsname #1%
+ \eappto\sseq@patchfor{\let\@nx#1 \@xp\@nx\csname sseq@\sseq@macroname#1\endcsname}%
+}
+
+
+
+\def\sseq@pgffor@recordarg#1#2{
+ \sseq@pgffor@modify#1
+ \@xp\pretocmd\csname sseq@\sseq@macroname#1\endcsname{\sseq@foreachcall\@xp{\the\sseq@foreachcall#2}}{}{\sseq@for@nopatch}
+}
+\def\sseq@pgffor@erecordarg#1#2{
+ \sseq@pgffor@modify#1
+ \@xp\pretocmd\csname sseq@\sseq@macroname#1\endcsname{\sseq@eval{\sseq@foreachcall{\the\sseq@foreachcall#2}}}{}{\sseq@for@nopatch}
+}
+
+
+\bgroup\lccode`\*=`\#\lowercase{\egroup
+
+% Modify the foreach argument parser commands to put the call into \sseq@foreachcall and to tell us what the variables are
+\def\sseq@pgffor@@vars@opt[#1]{\sseq@foreachcall\@xp{\the\sseq@foreachcall#1}\pgfkeys{/sseqpages/foreach/.cd,#1}\pgffor@vars}
+
+\sseq@pgffor@recordarg\pgffor@@vars{*1}
+\pretocmd\sseq@pgffor@@vars{\sseq@for@savemacro*1}{}{\sseq@for@nopatch}
+
+\sseq@pgffor@recordarg\pgffor@@vars@slash@gobble{/}
+\sseq@pgffor@recordarg\pgffor@macro@list{in *1}
+\pretocmd\sseq@pgffor@macro@list{\sseq@tempiffalse}{}{\sseq@for@nopatch}% Don't add this again in \pgffor@normal@lis
+
+\sseq@pgffor@modify\pgffor@normal@list
+% Add list to argument if it wasn't a macro
+\pretocmd\sseq@pgffor@normal@list{\ifsseq@tempif\sseq@foreachcall\@xp{\the\sseq@foreachcall in {*1}}\fi\sseq@tempiftrue}{}{\sseq@for@nopatch}
+
+\sseq@pgffor@recordarg\pgffor@collectforeach@macro{\foreach}
+\sseq@pgffor@recordarg\pgffor@collectforeach@normal{\foreach}
+
+\sseq@pgffor@modify\pgffor@iterate
+\pretocmd\sseq@pgffor@iterate{\sseq@opushstacktrace{\the\sseq@foreachcall}\sseq@thiscalltoks\@xp{\the\sseq@foreachcall}}{}{\sseq@for@nopatch}
+
+\sseq@pgffor@modify\pgffor@doloop
+\sseq@pgffor@modify\pgffor@invokebody
+\patchcmd\sseq@pgffor@doloop{\pgffor@begingroup}{\pgffor@begingroup\sseq@xsetlastcall{\the\sseq@foreachcall}}{}{\sseq@for@nopatch}
+\patchcmd\sseq@pgffor@invokebody{\pgffor@begingroup}{\pgffor@begingroup\sseq@xsetlastcall{\the\sseq@foreachcall}}{}{\sseq@for@nopatch}
+}
+
+%TODO: also hook \pgffor@assign@parse, \pgffor@remember@parse, \pgffor@count@parse.
+
diff --git a/graphics/pgf/contrib/spectralsequences/src/sseqkeys.code.tex b/graphics/pgf/contrib/spectralsequences/src/sseqkeys.code.tex
new file mode 100644
index 0000000000..03c8c7591a
--- /dev/null
+++ b/graphics/pgf/contrib/spectralsequences/src/sseqkeys.code.tex
@@ -0,0 +1,2131 @@
+%%
+%% Package: spectralsequences v1.3.0 2021-07-18
+%% Author: Hood Chatham
+%% Email: hood@mit.edu
+%% Date: 2021-07-18
+%% License: Latex Project Public License
+%%
+%% File: sseqkeys.code.tex
+%% Exposes: \sseqset
+%%
+%% Sets up the keys for sseqpages. There are a lot of them...
+%%
+
+
+% Run \pgfqkeys{directory}{keys} but for a list of directories. Helps to avoid code duplication.
+\def\sseq@pgfqkeysdirlist#1#2{%
+ \sseq@temptoks{#2}%
+ \sseq@pgfqkeysdirlist@#1,\sseq@nil
+}
+
+\def\sseq@pgfqkeysdirlist@#1,{%
+ \sseq@eval{\@nx\pgfqkeys{#1}{\the\sseq@temptoks}}%
+ \@ifnextchar\sseq@nil{\@gobble}{\sseq@pgfqkeysdirlist@}%
+}
+
+%%%% Interface macros:
+% These are the things that are used outside of this section:
+\let\sseq@pgfkeys@error@save\pgfkeys@error
+\pgfkeys{/sseqpages/userstyles/.unknown/.code={\sseq@passoptionto{\sseq@sseqsetdirectory}}}
+\protected\def\sseqset#1{\pgfqkeys{/sseqpages/userstyles}{#1}}
+
+\def\sseq@sseqsetdirectory{/sseqpages/global/default}
+\def\sseq@set{\pgfqkeys{/sseqpages}}
+
+%%%
+%%% Local Keys
+%%%
+
+% This is where and how we are going to store the results of our local options. Note that definitions need to be local to allow recursion.
+\def\sseq@savedoptioncode{}
+\def\sseq@d@addto@options{\sseq@d@addto@macro\sseq@savedoptioncode}
+\def\sseq@e@addto@options{\sseq@e@addto@macro\sseq@savedoptioncode}
+
+% #1 -- name of key
+% #2 -- options
+% This is the main way that we use options. (We also use \pgfqkeys{/sseqpages/global} and some similar things)
+% Dumps out code that the option indexes into \sseq@savedoptioncode
+% This is because \pgfkeys has a high performance cost, so instead of using \pgfkeys once per page,
+% we use this once when putting together the spectral sequence and save the result.
+\def\sseq@processoptions#1#2{%
+ \def\sseq@savedoptioncode{}%
+ \sseq@protectedeval{\@nx % TODO: get rid of this protectedeval
+ \pgfqkeys{/sseqpages/#1}{#2}
+ }%
+}
+
+% First pass versus second pass:
+% Lots of options mainly do stuff on the drawing run, but still need to warn the sseq setup algorithms that they're around
+% on the first pass. Some only do things on the first pass or only do things on the second pass.
+% For example, shifts: shifts apply a coordinate transform on the second pass, but also need to factor into the coordinates of
+% classes etc which are computed at specification time not at draw time. So xshift=2 increases \sseq@x by 2 on its first pass and
+% applies a coordinate transform by 2cm on the second pass.
+
+% normal mode does the firstpass options now and saves the secondpass options in \sseq@savedoptioncode for later use.
+% firstpass mode does the firstpass options and throws away the secondpass options.
+% secondpass mode throws away the firstpass options and does the secondpass options NOW
+% bothpass mode does both firstpass and secondpass options NOW
+% We spend the vast majority of our time in normal mode.
+
+\def\sseq@options@use#1{#1}
+\def\sseq@options@normalmode{\let\sseq@options@firstpass\sseq@options@use\let\sseq@options@secondpass\sseq@d@addto@options}
+\def\sseq@options@firstpassmode{\let\sseq@options@firstpass\sseq@options@use\let\sseq@options@secondpass\@gobble}
+\def\sseq@options@secondpassmode{\let\sseq@options@firstpass\@gobble\let\sseq@options@secondpass\sseq@options@use}
+\def\sseq@options@bothpassmode{\let\sseq@options@firstpass\sseq@options@use\let\sseq@options@secondpass\sseq@options@use}
+\sseq@options@normalmode
+
+\pgfqkeys{/handlers}{
+ .sseq @ first pass code/.code={
+ \pgfkeysalso{\pgfkeyscurrentpath/.code={\sseq@options@firstpass{#1}}}
+ },
+ .sseq @ first pass append code/.code={
+ \pgfkeysalso{\pgfkeyscurrentpath/.append code={\sseq@options@firstpass{#1}}}
+ },
+ .sseq @ second pass code/.code={
+ \pgfkeysalso{\pgfkeyscurrentpath/.code={\sseq@options@secondpass{#1}}}
+ },
+ .sseq @ second pass append code/.code={
+ \pgfkeysalso{\pgfkeyscurrentpath/.append code={\sseq@options@secondpass{#1}}}
+ },
+ .sseq @ second pass store in/.code={
+ \pgfkeysalso{\pgfkeyscurrentpath/.code={\sseq@options@secondpass{\def#1{##1}}}}
+ },
+ .sseq @ code/.code 2 args={
+ \pgfkeysalso{\pgfkeyscurrentpath/.code={\sseq@options@firstpass{#1}}}
+ \pgfkeysalso{\pgfkeyscurrentpath/.append code={\sseq@options@secondpass{#1}}}
+ },
+ .sseq @ transform/.code={
+ \sseq@keys@lastpart
+ \edef\sseq@temppath{\pgfkeyscurrentpath/\sseq@temp}
+ \pgfkeysalso{\sseq@temppath/.code/.expanded={\@nx\sseq@checktransform{\sseq@temp}\unexpanded{{##1}}}}
+ \pgfkeysalso{\sseq@temppath/.sseq @ second pass append code/.expanded={\@nx\sseq@pgfkeysdocommand{\sseq@temp}\unexpanded{{##1}}}}%
+ },
+ .sseq @ stdparse do transform/.code={
+ \sseq@keys@lastpart
+ \edef\sseq@temppath{\pgfkeyscurrentpath/\sseq@temp}
+ \ifx\pgfkeyscurrentvalue\pgfkeysnovalue@text
+ \pgfkeysalso{\sseq@temppath/.code/.expanded={\@nx\sseq@standardparse\@nx\sseq@options@secondpass{\@nx\sseq@pgfkeysdocommand{\sseq@temp}\unexpanded{{##1}}}}}
+ \else
+ \pgfkeysalso{\sseq@temppath/.code/.expanded={\@nx\sseq@standardparse\@nx\sseq@options@secondpass{\@nx\sseq@pgfkeysdocommand{\sseq@temp}{#1}}}s}
+ \fi
+ },
+ .sseq @ illegal/.code={
+ \sseq@keys@lastpart
+ \edef\sseq@temppath{\pgfkeyscurrentpath/\sseq@temp}
+ \pgfkeysalso{\sseq@temppath/.code/.expanded={\@nx\sseq@error@n{option-illegal}{\sseq@temp}}}
+ },
+ .sseq @ illegal as global/.code={
+ \sseq@keys@lastpart
+ \pgfkeysalso{/sseqpages/global/\sseq@temp/.code/.expanded={\@nx\sseq@error@n{option-illegal-as-global}{\sseq@temp}}}
+ },
+ .sseq @ global only/.code={
+ \sseq@keys@lastpart
+ \pgfkeysalso{/sseqpages/\sseq@temp/.code/.expanded={\@nx\sseq@error@n{option-global-only}{\sseq@temp}}}
+ }
+}
+
+
+%%% Some easy internal macros
+
+% This is for .unknown handlers. Try same key in different directory #1.
+\def\sseq@passoptionto#1{\sseq@eval{\@nx\pgfqkeys{#1}{\pgfkeyscurrentname={\unexpanded\@xp{\pgfkeyscurrentvalue}}}}}
+
+% Immediately execute a key that is either a tikz or pgf key.
+% #1 -- key
+% #2 -- value
+\def\sseq@pgfkeysdocommand#1#2{
+ \sseq@tempiffalse
+ \sseq@pgfkeysdocommand@try{/tikz}{#1}{#2}
+ \sseq@pgfkeysdocommand@try{/pgf}{#1}{#2}
+ \ifsseq@tempif\else\sseq@error@internal@nn{do-command-failed}{#1}{#2}\fi
+}
+
+% Execute a key if \sseq@tempif is true and it is defined, if so set \sseq@tempif to false.
+% These are meant to cascade.
+% #1 -- directory
+% #2 -- key
+% #3 -- value
+\def\sseq@pgfkeysdocommand@try#1#2#3{
+ \ifsseq@tempif\else
+ \pgfkeysifdefined{#1/#2/.@cmd}{
+ \sseq@tempiftrue
+ \def\pgfkeysdefaultpath{#1/}
+ \edef\pgfkeyscurrentkey{#1/#2}
+ \pgfkeysgetvalue{#1/#2/.@cmd}{\pgfkeys@code}%
+ \pgfkeys@code#3\pgfeov
+ }{
+ \pgfkeysifdefined{#1/#2}{
+ \sseq@tempiftrue
+ \pgfkeyssetvalue{#1/#2}{#3}
+ }{}
+ }
+ \fi
+}
+
+%%%
+% Our main unknown option handler
+% This is copied from the standard tikz .unknown handler, but instead of running the code it stores it in \sseq@savedoptioncode.
+% When it tries the key in /tikz and in /pgf it also recursively expands \pgfkeysalso's.
+
+\def\sseq@keys@unknown@error@normal{\sseq@error{unknown-key}} % a hook so we can turn off errors if we like
+\let\sseq@keys@unknown@error\sseq@keys@unknown@error@normal % This is used in .sseq style
+\let\sseq@add@tikzkey@to@options\sseq@e@addto@options % another hook for .sseq style
+
+\def\sseq@keys@unknown{%
+ \let\tikz@key\pgfkeyscurrentname
+ \sseq@keys@try@addtooptions{/sseqpages/userstyles,/tikz,/pgf}{}{%
+ \@xp\pgfutil@in@\@xp!\@xp{\tikz@key}%
+ \ifpgfutil@in@%
+ % this is a color!
+ \sseq@keys@unknown@handlecolor
+ \else%
+ \pgfutil@doifcolorelse{\tikz@key}%
+ {%
+ \sseq@keys@unknown@handlecolor
+ }%
+ {%
+ % Ok, second chance: This might be an arrow specification:
+ \@xp\pgfutil@in@\@xp-\@xp{\tikz@key}%
+ \ifpgfutil@in@%
+ % Ah, an arrow spec!
+ \sseq@add@tikzkey@to@options{\@nx\sseq@options@secondpass{\@nx\tikz@processarrows{\tikz@key}}}%
+ \else%
+ % Ok, third chance: A shape!
+ \@xp\ifx\csname pgf@sh@s@\tikz@key\endcsname\relax%
+ \sseq@keys@unknown@error%
+ \else%
+ \sseq@add@tikzkey@to@options{\@nx\sseq@options@secondpass{\def\@nx\tikz@shape{\tikz@key}}}%
+ \fi%
+ \fi%
+ }%
+ \fi%
+ }%
+}
+
+
+\def\sseq@keys@unknown@handlecolor{%
+ \sseq@add@tikzkey@to@options{\@nx\sseq@options@secondpass{\@nx\tikz@addoption{\@nx\tikz@compat@color@set{\tikz@key}}}}%
+ \sseq@add@tikzkey@to@options{\@nx\sseq@options@secondpass{\def\@nx\tikz@textcolor{\tikz@key}}}%
+}
+
+%%% \sseq@keys@try@addtooptions
+% #1 -- a comma separated list of directories
+% #2 -- success code
+% #3 -- failure code
+%
+% This is for the main .unknown handler and for \sseq@addstylecodetooptions (which pretends to be \pgfkeys executed in a directory with only a .unknown)
+% Tries to run the current key (\pgfkeyscurrentname=\pgfkeyscurrentvalue) in each of the directories in turn.
+% If it finds the key in one of the directories, recursively expand the \pgfkeysalso in the code,
+% add the final result to \sseq@savedoptioncode, and run success code. Otherwise run failure code.
+%
+% The goal is to avoid storing any commands that require the use of pgfkeys.
+% This is only a partial solution, because it misses \tikzset, \pfgqkeys, etc.
+% \pgfkeysalso is particularly dangerous though because we might not remember what directory it was supposed to happen in.
+% In order to get the recursive expansion of \pgfkeysalso's we need to modify the pgfkeys algorithm so that it
+% passes control back to \sseq@keys@addtooptions@checkalso when it's done.
+% Modifies tikz commands \pgfkeys@case@one and \pgfkeys@case@two@extern from file /pgf/utilities/pgfkeys.code.tex lines 352 and 365
+\def\sseq@keys@try@addtooptions#1{%
+ \sseq@keys@try@addtooptions@#1,\sseq@nil
+}
+\def\sseq@keys@try@addtooptions@#1,{%
+ \bgroup
+ \def\pgfkeysdefaultpath{#1/}%
+ \def\next{\egroup\@ifnextchar\sseq@nil{\@xp\@secondoftwo\@gobble}{\sseq@keys@try@addtooptions@}}%
+ \pgfkeysifdefined{#1/\pgfkeyscurrentname/.@cmd}{%
+ \edef\pgfkeyscurrentkey{#1/\pgfkeyscurrentname}%
+ \ifx\pgfkeyscurrentvalue\pgfkeysnovalue@text% Hmm... no value
+ \pgfkeysifdefined{#1/\pgfkeyscurrentname/.@def}%
+ {\pgfkeysgetvalue{#1/\pgfkeyscurrentname/.@def}{\pgfkeyscurrentvalue}}%
+ {}% no default, so leave it
+ \fi%
+ \pgfkeysgetvalue{#1/\pgfkeyscurrentname/.@cmd}{\pgfkeys@code}%
+ \sseq@eval{\@nx\sseq@keys@addtooptions@checkalso{\unexpanded\@xptwo{\@xp\pgfkeys@code\pgfkeyscurrentvalue\pgfeov}}}%
+ \sseq@smuggle@macro\sseq@savedoptioncode
+ \egroup
+ \def\next{\@xp\@firstoftwo\sseq@gobble@to@nil}%
+ }{%
+ \pgfkeysifdefined{#1/\pgfkeyscurrentname}{%
+ \egroup
+ \sseq@add@tikzkey@to@options{\@nx\sseq@options@secondpass{\@nx\pgfkeyssetvalue{#1/\pgfkeyscurrentname}{\unexpanded\@xp{\pgfkeyscurrentvalue}}}}%
+ \def\next{\@xp\@firstoftwo\sseq@gobble@to@nil}%
+ }{}%
+ }%
+ \next
+}
+
+% Iterate over list and run \pgfkeysalso's when we find them. Note that we use our variants of \pgfkeys@case@... which hand control back to this command,
+% so this is recursive -- the result is guaranteed not to have any instance of \pgfkeysalso in it!
+\def\sseq@keys@addtooptions@checkalso#1{%
+ \let\pgfkeys@case@one\sseq@pgfkeys@case@one@store
+ \let\pgfkeys@case@two@extern\sseq@pgfkeys@case@two@extern@store
+ \sseq@keys@addtooptions@checkalso@#1\pgfkeysalso\sseq@nil
+}
+
+\def\sseq@keys@addtooptions@checkalso@#1\pgfkeysalso#2{%
+ \def\sseq@tempa{/tikz/}%
+ \def\sseq@tempb{/pgf/}%
+ % This is an extra hook I needed to add so that my own keys can execute code when they need to...
+ \sseq@keys@addtooptions@checkdo#1\sseq@keys@do\sseq@nil
+ \ifx\sseq@nil#2% We're done, restore pgfkeys.
+ \let\pgfkeys@case@one\sseq@pgfkeys@case@one@save
+ \let\pgfkeys@case@two@extern\sseq@pgfkeys@case@two@extern@save
+ \else
+ \ifx\expandafter#2 % if they wanted to expand the first token, handle that
+ \sseq@add@tikzkey@to@options{\sseq@options@secondpass@iftikz{\relax\relax}}% The last token was an \expandafter, we need to keep it from doing anything
+ \@xptwo\sseq@keys@addtooptions@checkalso@xp
+ \else
+ \pgfkeysalso{#2}%
+ \@xptwo\sseq@keys@addtooptions@checkalso@
+ \fi
+ \fi
+}
+
+\def\sseq@keys@addtooptions@checkalso@xp#1{%
+ \@xp\pgfkeysalso\@xp{#1}%
+ \sseq@keys@addtooptions@checkalso@
+}
+
+% Used to get collections code to run right.
+\def\sseq@keys@addtooptions@checkdo#1\sseq@keys@do#2{%
+ \sseq@add@tikzkey@to@options{\sseq@options@secondpass@iftikz{\unexpanded{#1}}}%
+ \ifx#2\sseq@nil\else
+ #2%
+ \@xp\sseq@keys@addtooptions@checkdo
+ \fi
+}
+
+
+% This has to be expandable, so \sseq@tempa and \sseq@tempb are set to "/tikz/" and "/pgf/" ahead of time
+% Defer the command if it is in directories /pgf or /tikz, otherwise run it now. TODO: explain here why we need this.
+\def\sseq@options@secondpass@iftikz{%
+ \ifx\pgfkeysdefaultpath\sseq@tempa
+ \@nx\sseq@options@secondpass
+ \else
+ \ifx\pgfkeysdefaultpath\sseq@tempb
+ \@nx\sseq@options@secondpass
+ \else
+ \@xptwo\@firstofone
+ \fi
+ \fi
+}
+
+
+% Here we hook into pgfkeys. This is remarkably easy!
+\let\sseq@pgfkeys@case@one@save\pgfkeys@case@one
+\let\sseq@pgfkeys@case@two@extern@save\pgfkeys@case@two@extern
+\def\sseq@pgfkeys@case@one@store{%
+ \pgfkeysifdefined{\pgfkeyscurrentkey/.@cmd}{%
+ \pgfkeysgetvalue{\pgfkeyscurrentkey/.@cmd}{\pgfkeys@code}%
+ % The following line is the only change, it used to be \@xp\pgfkeys@code\pgfkeyscurrentvalue\pgfeov which would just run the code.
+ % Instead we recursively expand the \pgfkeysalso's and then store in it \sseq@savedoptioncode
+ \sseq@eval{\@nx\sseq@keys@addtooptions@checkalso{\unexpanded\@xptwo{\@xp\pgfkeys@code\pgfkeyscurrentvalue\pgfeov}}}%
+ }{\pgfkeys@case@two}%
+}
+\def\sseq@pgfkeys@case@two@extern@store{%
+ \ifx\pgfkeyscurrentvalue\pgfkeysnovalue@text%
+ \pgfkeysvalueof{\pgfkeyscurrentkey}% this probably isn't a real case
+ \else%
+ % The following line is the only change, it used to be (roughly) \pgfkeyssetvalue{\pgfkeyscurrentkey}{\pgfkeyscurrentvalue}
+ % which would immediately set the value. Instead we store in it \sseq@savedoptioncode
+ \sseq@add@tikzkey@to@options{\@nx\sseq@options@secondpass{\@nx\pgfkeyssetvalue{\pgfkeyscurrentkey}{\unexpanded\@xp{\pgfkeyscurrentvalue}}}}%
+ \fi%
+}
+
+\pgfkeys{%
+ /sseqpages/.is family,
+ /sseqpages/.unknown/.code={\sseq@keys@unknown},
+%
+ /sseqpages/class/.unknown/.code={%
+ \expandafter\ifx\csname pgf@sh@s@\pgfkeyscurrentname\endcsname\relax % is it the name of a shape?
+ \sseq@passoptionto{/sseqpages}%
+ \else
+ \sseq@e@addto@options{\def\@nx\tikz@shape{\pgfkeyscurrentname}}% if so, store it
+ \fi
+ },
+ /sseqpages/.is family,
+ /sseqpages/scope/.is family,
+ /sseqpages/scope/.unknown/.code={\sseq@passoptionto{/sseqpages}},
+ /sseqpages/class/.is family,
+ /sseqpages/class/label/.is family,
+ /sseqpages/class/label/.unknown/.code={\sseq@keys@unknown},
+ /sseqpages/class/label/pin/.unknown/.code={\sseq@keys@unknown},
+ /sseqpages/class/node texts/.is family,
+ /sseqpages/class/node texts/.unknown/.code={\sseq@passoptionto{/tikz}},
+ /sseqpages/differential/.is family,
+ /sseqpages/differential/.unknown/.code={\sseq@passoptionto{/sseqpages}},
+ /sseqpages/struct line/.is family,
+ /sseqpages/struct line/.unknown/.code={\sseq@passoptionto{/sseqpages}},
+ /sseqpages/label/.is family,
+ /sseqpages/label/.unknown/.code={\sseq@keys@unknown},
+ /sseqpages/tikz primitives/.is family,
+ /sseqpages/tikz primitives/.unknown/.code={\sseq@keys@unknown},
+ % One off directories
+ /sseqpages/fit/.is family,
+ /sseqpages/fit/.unknown/.code={\sseq@passoptionto{/sseqpages}},
+ /sseqpages/foreach/.unknown/.code={\sseq@passoptionto{/pgf/foreach}},
+ /sseqpages/class placement/.unknown/.code={\sseq@passoptionto{/tikz}}
+}
+
+\pgfqkeys{/sseqpages/class}{shape/.code={\edef\tikz@shape{\pgfkeyscurrentname}}} % shape=whatever shape passing
+
+\newif\ifsseq@pin
+\pgfqkeys{/sseqpages/class/label}{% maybe implement multipart node labels this way? No way!
+ node/.sseq @ first pass code={\sseq@classlabelfalse},
+ inside/.sseq @ first pass code={\sseq@classlabelfalse},
+ pin/.sseq @ first pass code = {
+ \let\sseq@savedoptioncode@save\sseq@savedoptioncode
+ \sseq@processoptions{class/label/pin}{#1}
+ \edef\sseq@savedoptioncode{\unexpanded\@xp{\sseq@savedoptioncode@save}\@nx\sseq@d@addto@macro\@nx\sseq@pinoptions{\unexpanded\@xp{\sseq@savedoptioncode}}}
+ },
+ pin/.sseq @ second pass append code = {\sseq@pintrue}
+}
+\def\sseq@pinoptions{}
+
+%% label directions
+\def\sseq@labeldirectionlist{\\{above}\\{below}\\{left}\\{right}\\{above left}\\{above right}\\{below left}\\{below right}}
+\def\sseq@setlabeldirectionkeys#1{
+ \@ifnextchar[{\sseq@setlabeldirectionkeys@{#1}}{\sseq@setlabeldirectionkeys@{#1}[/.code]}%]
+}
+\def\sseq@setlabeldirectionkeys@#1[#2]#3{
+ \bgroup
+ \def\sseq@temp##1{#3}
+ \sseq@temptoks{}
+ \def\\##1{
+ \sseq@eval{\sseq@temptoks{\the\sseq@temptoks##1#2={\unexpanded\@xp{\sseq@temp{##1}}},}}
+ }
+ \sseq@labeldirectionlist
+ \sseq@eval{\egroup\@nx\pgfqkeys{#1}{\the\sseq@temptoks}}
+}
+\begingroup
+\def\\#1{\@xp\sseq@addtostorelist\csname sseq@default@#1@distance\endcsname{1ex}}
+\sseq@labeldirectionlist
+\endgroup
+\def\sseq@defaultlabeldistance#1{\@xp\csname sseq@default@#1@distance\endcsname}
+\def\sseq@setdefaultlabeldistance#1#2{\sseq@savedpaths@add{\@xp\gdef\csname sseq@default@#1@distance\endcsname{#2}}}
+\def\sseq@setdefaultlabeldistances#1{\def\\##1{\sseq@setdefaultlabeldistance{##1}{#1}}\sseq@labeldirectionlist}
+
+
+
+\sseq@setlabeldirectionkeys{/sseqpages/class/label}[/.sseq @ first pass code]{\sseq@classlabeltrue}
+\sseq@setlabeldirectionkeys{/sseqpages/class/label}[/.sseq @ second pass append code]{\sseq@class@positionlabel{#1}{##1}}
+\sseq@setlabeldirectionkeys{/sseqpages/class/label}[/.default]{\sseq@defaultlabeldistance{#1}}
+\sseq@setlabeldirectionkeys{/sseqpages/global/class/label}[/.default]{\sseq@defaultlabeldistance{#1}}
+
+\sseq@setlabeldirectionkeys{/sseqpages/global}[ label distance/.code]{\sseq@setdefaultlabeldistance{#1}{##1}}
+\sseq@setlabeldirectionkeys{/sseqpages}[ label distance/.code]{\sseq@setdefaultlabeldistance{#1}{##1}}
+
+\sseq@pgfqkeysdirlist{/sseqpages/global,/sseqpages}{
+ label distance/.code={\sseq@setdefaultlabeldistances{#1}}
+}
+
+\def\sseq@class@positionlabel#1#2{\ifx\tikz@fig@name\undefined\else\sseq@pgfkeysdocommand{#1}{#2 of \tikz@fig@name}\fi}
+
+
+
+\sseq@addtostorelist\sseq@classlabel@handler{} % \sseq@classlabel@handler is public via "class label handler"
+\def\sseq@classlabel@handler@default#1{\def\result{#1}}
+\sseq@addtostorelist\sseq@classname@handler{} % \sseq@classname@handler is public via "class name handler"
+\def\sseq@classname@handler@default#1{\def\result{#1}}
+
+\def\sseq@labeltextfn#1{#1} % labeltextfn is private
+\def\sseq@labeltextfn@rotatescale#1{%
+ \@nx\ifmmode
+ \@nx\scalebox{\sseq@labletext@scale@factor}{\@nx\rotatebox{\sseq@labletext@rotate@angle}{$#1$}}%
+ \@nx\else
+ \@nx\scalebox{\sseq@labletext@scale@factor}{\@nx\rotatebox{\sseq@labletext@rotate@angle}{#1}}%
+ \@nx\fi
+}
+\def\sseq@labletext@rotate@angle{0}
+\def\sseq@labletext@scale@factor{1}
+
+\bgroup\lccode`8=`p\lccode`9=`t\lowercase{\egroup
+\def\sseq@eatpt#189{#1}
+}
+
+\sseq@pgfqkeysdirlist{/sseqpages/label,/sseqpages/class/label}{
+ rotate/.code={%
+ \let\sseq@labeltextfn\sseq@labeltextfn@rotatescale
+ \edef\sseq@labletext@rotate@angle{\@xp\sseq@eatpt\the\dimexpr\sseq@labletext@rotate@angle pt+#1pt}%
+ },
+ scale/.code={%
+ \let\sseq@labeltextfn\sseq@labeltextfn@rotatescale
+ \edef\sseq@labletext@scale@factor{\@xp\sseq@eatpt\the\dimexpr\sseq@labletext@scale@factor pt*#1}%
+ },
+% shift/.sseq @ second pass code={\pgfkeysalso{xshift=\sseq@shift@getx#1}\pgfkeysalso{yshift=\sseq@shift@gety#1}}
+}
+
+
+\pgfqkeys{/sseqpages/global}{
+ background color/.initial=white
+}
+\pgfqkeys{/sseqpages/label}{
+ description/.code={%
+ \edef\sseq@edgeoptionhook{\@nx\pgfkeys{/tikz/fill=\pgfkeysvalueof{/sseqpages/global/background color}}}%
+ },
+ description/.sseq @ second pass append code={%
+ \pgfkeysalso{/tikz/anchor=center}%
+ }
+}
+
+\def\sseq@edgeoptionhook{}% This is just to make description work.
+
+%% Labels
+%% The code for this that actually does most of the work is inside of sseqmain.code.tex -- search for "Labels"
+%% This just handles the interface with the keyvalue system here.
+
+
+%% Quote handling
+% \sseq@handlequote will be installed later as the 'first char syntax' for ".
+% Problem is that 'first char syntax' isn't allowed to care what directory it is in, but we need different handling
+% for classes versus edges versus scopes. Solution is to use \pgfkeysalso and then define the handler in a key.
+\def\sseq@protectlabel{\@nx\sseq@protectlabel\@nx}
+\def\sseq@handlequote#1{\pgfkeysalso{handle quote=#1}}
+\pgfkeys{%
+ /sseqpages/class/handle quote/.code={%
+ \let\tikz@quotes@as@save\tikz@quotes@as
+ \let\tikz@quotes@as\sseq@handleclassquotes
+ \tikz@quote@parser{#1}%
+ \let\tikz@quotes@as\tikz@quotes@as@save
+ },
+ /sseqpages/handle quote/.code={%
+ \let\tikz@quotes@as\sseq@handleedgequotes
+ \tikz@quote@parser{#1}%
+ \let\tikz@quotes@as\tikz@quotes@as@save
+ }
+}
+
+% We just do the parsing in sseqkeys, \sseq@handleclassquotes@inner which does the main work is defined sseqmain.
+\def\sseq@handlescopequotes#1#2{% We use this via \let\tikz@quotes@as\sseq@handleclassquotes
+ /utils/exec={% so it's called inside \pgfkeysalso{stuff}. Need to surround with /utils/exec={} to get out.
+ \global\let\sseq@classnodetext\pgfutil@empty%
+ \global\let\sseq@classnodetextoptions\pgfutil@empty%
+ \global\let\sseq@classlabelnodes\pgfutil@empty%
+ \sseq@handleclassquotes@inner{#1}{#2}
+ }
+}
+
+% We use this via \let\tikz@quotes@as\sseq@handleclassquotes
+% so it's called inside \pgfkeysalso{stuff}. Need to surround with /utils/exec={} to get out.
+\def\sseq@handleclassquotes#1#2{/utils/exec={\sseq@d@addto@macro\sseq@processlabels{\sseq@handleclassquotes@inner{#1}{#2}}}}
+
+
+
+\def\sseq@checktransform@error{\sseq@error@nn{illegal-transform}}
+\let\sseq@checktransform\sseq@checktransform@error
+
+
+%% Parsing, background vs foreground, and context
+\def\sseq@standardparse{%
+ \let\sseq@tikzprimitives@coords@maybeclass\sseq@tikzprimitives@coords@notaclass
+ \let\sseq@checktransform\@gobbletwo
+ \let\sseq@tikz@shifttransform\sseq@okayshifttransform
+ % TODO: This needs fixing:
+ %((((((((((((((((((
+ \def\class##1){\sseq@setthiscall{\string\class##1)}\sseq@error@x{standard-parse}{\string\class}}%
+ \def\replaceclass##1){\sseq@setthiscall{\string\replaceclass##1)}\sseq@error@x{standard-parse}{\string\replaceclass}}%
+ \def\classoptions##1){\sseq@setthiscall{\string\classoptions##1)}\sseq@error@x{standard-parse}{\string\classoptions}}%
+ \def\d##1){\sseq@setthiscall{\string\d##1)}\sseq@error@x{standard-parse}{\string\d}}%
+ \def\doptions##1){\sseq@setthiscall{\string\doptions##1)}\sseq@error@x{standard-parse}{\string\doptions}}%
+ \def\structline##1)##2){\sseq@setthiscall{\string\structline##1)##2)}\sseq@error@x{standard-parse}{\string\structline}}%
+ \def\structlineoptions##1)##2){\sseq@setthiscall{\string\structlineoptions##1)##2)}\sseq@error@x{standard-parse}{\string\structlineoptions}}%
+}
+
+% I don't think this is ever actually used...
+\def\sseq@nodeparse{%
+ \let\sseq@tikzprimitives@coords@maybeclass\sseq@tikzprimitives@coords@maybeclass@save
+ \let\sseq@tikzprimitives@coords@maybeclass
+ \def\sseq@transform{\sseq@error@nn{illegal-transform}} % Disallow most coordinate transforms
+ \let\sseq@shifttransform\sseq@checkshifttransform
+ \let\class\sseq@class
+ \let\replaceclass\sseq@replaceclass
+ \let\structline\sseq@structline
+}
+
+\sseq@pgfqkeysdirlist{/sseqpages,/sseqpages/tikz primitives}{%
+ standard parse/.code={
+ \let\sseq@firstpass@shifttransform\@gobbletwo
+ \sseq@d@addto@options{\sseq@standardparse}
+ \pgfkeys{/sseqpages/tikz primitives/shift/.sseq @ second pass code={\tikzset{shift={##1}}}}
+ },
+ node parse/.code={ % This is never used, probably broken, and likely shouldn't exist
+ \let\sseq@firstpass@shifttransform\sseq@firstpass@checkshifttransform
+ \let\sseq@checktransform\sseq@checktransform@error
+ \sseq@d@addto@options{\sseq@nodeparse}
+ },
+ background/.code={\sseq@setsavedpaths{background}\pgfkeysalso{standard parse}},
+ foreground/.code={\ifsseq@keepchanges\sseq@setsavedpaths{standard}\else\sseq@setsavedpaths{temporary}\fi\pgfkeysalso{node parse}}
+}
+
+\pgfkeys{
+ /sseqpages/tikz primitives/handle quote/.sseq @ second pass code={\tikzset{#1}},
+ /tikz/handle quote/.code={\sseq@error{tikz-edge-quotes}}
+}
+
+
+\sseq@pgfqkeysdirlist{/sseqpages/class,/sseqpages/differential}{
+ standard parse/.sseq @ first pass code={\sseq@error@n{option-tikz-prims-only}{standard parse}},
+ background/.sseq @ first pass code={\sseq@error@n{option-tikz-prims-only}{background}}
+}
+
+% Some class options. All of these will make changes local to the current \class command. They are communicated to \class via these commands,
+% which we initialize here as empty.
+\def\sseq@class@tag{}
+\def\sseq@class@tagprefix{}
+\def\sseq@class@name{}
+\def\sseq@namehandler{}
+\def\sseq@currenttag{\sseq@class@tag}% for the user ??
+\def\sseq@tooltip{}
+\pgfqkeys{/sseqpages/scope}{
+ tag/.code={\pgfkeys@spdef\sseq@class@tag{#1}},
+ tag prefix/.code={\pgfkeys@spdef\temp{#1}\sseq@e@addto@macro\sseq@class@tagprefix{\unexpanded\@xp{#1}}}
+}
+
+\def\sseq@classinsert{}
+\def\sseq@classnameprefix{}
+\def\sseq@classnamepostfix{}
+\pgfqkeys{/sseqpages}{
+ class name prefix/.store in=\sseq@classnameprefix,
+ class name postfix/.store in=\sseq@classnamepostfix
+}
+
+\pgfqkeys{/sseqpages/class}{
+ name/.code={%
+ \sseq@protectedeval{\@nx\sseq@classname@handler{\sseq@classnameprefix#1\sseq@classnamepostfix}}%
+ \edef\sseq@class@name{\unexpanded\@xp{\result}}%
+ \pgfkeysalso{/sseqpages/alias/.expand once=\detokenize\@xp{\sseq@class@name}}%
+ },
+ show name/.store in=\sseq@class@showname,
+ page/.code={\sseq@getnumrange@defaultupperboundinfty\sseq@class@page{#1}},% page & gen are only for \classoptions.
+ generation/.code={\sseq@getnumrange@defaultupperboundinfty\sseq@gen{#1}}, % \class tests if \sseq@class@page is set and throw an error if so.
+ tag/.code={\sseq@d@addto@macro\sseq@class@tag{#1}},
+ offset/.store in=\sseq@tempoffset,
+ tooltip/.sseq @ first pass code={\ifx\sseqtooltip\undefined \sseq@error{no-tooltip}\else \def\sseq@tooltip{#1}\fi},
+ insert/.store in=\sseq@classinsert
+}
+
+\pgfqkeys{/sseqpages/struct line}{
+ page/.code={\sseq@getnumrange@defaultupperboundinfty\sseq@structline@page{#1}}
+}
+
+\def\sseq@fitalso{}
+\pgfqkeys{/sseqpages/fit}{
+ fit/.sseq @ first pass code={\sseq@d@addto@macro\sseq@fitalso{#1}},
+ page/.code={\sseq@getnumrange@defaultupperboundinfty\sseq@fit@page{#1}}
+}
+
+\def\sseq@getnumrange#1#2{%
+ \pgfutil@in@{--}{#2}\ifpgfutil@in@
+ \sseq@getnumrange@range#1#2\sseq@nil
+ \else
+ \sseq@getnumrange@range#1#2--#2\sseq@nil
+ \fi
+}
+
+\def\sseq@getnumrange@defaultupperboundinfty#1#2{%
+ \pgfutil@in@{--}{#2}\ifpgfutil@in@
+ \sseq@getnumrange@range#1#2\sseq@nil
+ \else
+ \sseq@getnumrange@range#1#2--\sseq@infinity\sseq@nil
+ \fi
+}
+
+\def\sseq@getnumrange@range#1#2--#3\sseq@nil{%
+ \ifx\infty#2
+ \edef#1{\sseq@infinity}%
+ \@xp\edef\csname\sseq@macroname#1max\endcsname{\sseq@infinity}%
+ \else
+ \edef#1{\the\numexpr#2}%
+ \ifx\infty#3
+ \@xp\edef\csname\sseq@macroname#1max\endcsname{\sseq@infinity}%
+ \else
+ \@xp\edef\csname\sseq@macroname#1max\endcsname{\the\numexpr#3}%
+ \fi
+ \fi
+}
+
+
+
+%% Needs tikz
+% Force \class or \d to fall back to using tikz to render their output
+% Rather than using the faster direct \pgf primitives (largely copied from tikz to maximize compatibility)
+% Drawing with tikz is slower, but I don't want to reimplement these features
+\def\sseq@needstikzandpass#1#2{\sseq@needstikzandpass@{#1}#2,\@nil}
+\def\sseq@needstikzandpass@#1#2,{%
+ \pgfqkeys{#1}{#2/.code={\sseq@options@firstpass{\sseq@needstikztrue}\sseq@options@secondpass{\sseq@pgfkeysdocommand{#2}{##1}\sseq@needstikztrue}}}%
+ \@ifnextchar\@nil{\@gobble}{\sseq@needstikzandpass@{#1}}%
+}
+
+\pgfqkeys{/sseqpages}{needs tikz/.code=\sseq@needstikztrue}
+% All of these things need tikz to draw but otherwise require no special handling.
+\sseq@needstikzandpass{/sseqpages}{%
+ shade, blend mode, pattern, path picture, path fading, decorate, pin, label % should pin be here?
+}
+
+\sseq@needstikzandpass{/sseqpages/differential}{%
+ bend left, bend right, in, out, relative, looseness, in looseness, out looseness,
+ min distance, max distance, out min distance, out max distance, in min distance, in max distance, distance,
+ controls, in control, out control,
+ loop, loop above, loop below, loop left, loop right % maybe delete the loops and replace them with errors? It's pretty pointless to use these.
+}
+
+\sseq@needstikzandpass{/sseqpages/struct line}{%
+ bend left, bend right, in, out, relative, looseness, in looseness, out looseness,
+ min distance, max distance, out min distance, out max distance, in min distance, in max distance, distance,
+ controls, in control, out control,
+ loop, loop above, loop below, loop left, loop right % maybe delete the loops and replace them with errors? It's pretty pointless to use these.
+}
+
+% circle split/.code=\sseq@needstikzandpass,
+% circle solidus/.code=\sseq@needstikzandpass,
+% ellipse split/.code=\sseq@needstikzandpass,
+% rectangle split/.code=\sseq@needstikzandpass,
+% align/.code=\sseq@needstikzandpass,
+
+\sseq@addtostorelist\sseq@runoffarrow@start@differential@spec{}
+\sseq@addtostorelist\sseq@runoffarrow@end@differential@spec{>}
+\sseq@addtostorelist\sseq@runoffarrow@start@structline@spec{...}
+\sseq@addtostorelist\sseq@runoffarrow@end@structline@spec{...}
+\sseq@pgfqkeysdirlist{/sseqpages/global,/sseqpages}{
+ run off differentials/.code args={#1-#2}{\def\sseq@runoffarrow@start@differential@spec{#1}\def\sseq@runoffarrow@end@differential@spec{#2}},
+ run off struct lines/.code args={#1-#2}{\def\sseq@runoffarrow@start@structline@spec{#1}\def\sseq@runoffarrow@end@structline@spec{#2}},
+ run off/.forward to=/sseqpages/run off differentials,
+ run off/.forward to=/sseqpages/run off struct lines
+}
+
+
+\sseq@pgfqkeysdirlist{/sseqpages/differential,/sseqpages/struct line}{
+ source anchor/.sseq @ second pass store in=\sseq@edgesourceanchor,
+ target anchor/.sseq @ second pass store in=\sseq@edgetargetanchor,
+ shorten >/.sseq @ second pass code={\sseq@pgfkeysdocommand{shorten >}{#1}},
+ shorten </.sseq @ second pass code={\sseq@pgfkeysdocommand{shorten <}{#1}},
+}
+\pgfqkeys{/sseqpages/differential}{
+ /sseqpages/differential/invisible/.sseq @ first pass code={\sseq@drawdifferentialfalse}
+}
+\newif\ifsseq@drawdifferential
+\sseq@drawdifferentialtrue
+
+%%%
+%%% Global Keys
+%%%
+
+%%% Style options
+
+%% Add key code to \sseq@savedoptioncode
+% #1 -- a list of directories to search
+% #2 -- keys
+%
+% This is pretty similar in basic concept to \sseq@processoptions, but instead of using the .unknown handlers to walk up the directory,
+% it just tries the key in each directory in the list. I'm not actually sure anymore why it was important to do it this way, but there probably was a good reason...
+%
+% This is a faster and sloppier way of doing:
+% \def\sseq@temp{#1}
+% \pgfqkeys{/sseqpages/alwaysunknown}{#2}
+% and defining an event handler: alwaysunknown/.unknown/.code={\@xp\sseq@keys@try@addtooptions\@xp{\sseq@temp}{}{\sseq@keys@unknown}
+% That basically takes advantage of the pgfkeys parser but gives me back control over what to do with the parsed things.
+%
+% For some reason I made the probably misguided decision that that approach was too slow, even though this isn't performance
+% critical code. Maybe I had a better reason for doing this that I can't remember. Instead I copied pgfkeys but deleted all the
+% parts that are useless in this context, which is most of it. Occasionally bugs crop up here when it turns out I deleted a little too much.
+%
+% This uses a hook \sseq@addstylecodetooptions@unknown to allow the sseqs style to sift out the nonglobal keys and handle them differently.
+%
+% Copied with modification from /pgf/utilities/pfgkeys.code.tex
+
+\def\sseq@addstylecodetooptions#1#2{%
+ \def\sseq@directorylist{#1}%
+ \sseq@simple@pgfkeys@parse#2,\pgfkeys@mainstop
+}
+
+\def\sseq@simple@pgfkeys@parse{\futurelet\pgfkeys@possiblerelax\sseq@simple@pgfkeys@parse@main}
+\def\sseq@simple@pgfkeys@parse@main{%
+ \ifx\pgfkeys@possiblerelax\pgfkeys@mainstop%
+ \@xp\@gobble
+ \else%
+ \@xp\sseq@simple@pgfkeys@@normal
+ \fi%
+}
+
+\def\sseq@simple@pgfkeys@@normal#1,{%
+ \sseq@simple@pgfkeys@unpack#1=\pgfkeysnovalue=\pgfkeys@stop
+ \sseq@simple@pgfkeys@parse
+}
+
+\def\sseq@simple@pgfkeys@unpack#1=#2=#3\pgfkeys@stop{%
+ \pgfkeys@spdef\pgfkeyscurrentkey{#1}%
+ \pgfkeys@spdef\pgfkeyscurrentvalue{#2}%
+ \ifx\pgfkeyscurrentkey\pgfkeys@empty\else
+ \def\next{\@xp\sseq@keys@try@addtooptions\@xp{\sseq@directorylist}{}{\sseq@addstylecodetooptions@unknown}}%
+ % We deleted the general "first char syntax" handler, so we need to do our own check for the quote.
+ \sseq@simple@pgfkeys@checkquote#1\sseq@nil{%
+ \def\pgfkeyscurrentname{handle quote}%
+ \def\pgfkeyscurrentvalue{#1}%
+ }{%
+ \edef\pgfkeyscurrentkey{\pgfkeyscurrentkey}%
+ \ifx\pgfkeyscurrentkey\pgfkeys@empty
+ \let\next\relax
+ \else%
+ \let\pgfkeyscurrentname\pgfkeyscurrentkey
+ \fi
+ }%
+ \@xp\next
+ \fi
+}
+% This is a hook for the "sseqs" style.
+\let\sseq@addstylecodetooptions@unknown\sseq@keys@unknown
+
+\long\def\sseq@simple@pgfkeys@checkquote#1#2\sseq@nil{%
+ \ifx#1"%
+ \@xp\@firstoftwo
+ \else
+ \@xp\@secondoftwo
+ \fi
+}
+
+
+\def\sseq@stylelist{}
+\def\sseq@clearchangestyles{}
+
+% The basic idea here is we make a tokens list \sseq@structlinestyle and define a key "struct lines" that takes a list of keys
+% and adds the keys to \sseq@structlinestyle. In order to do this, I need some special trick to delete spaces.
+% The macro \sseq@setmacronospaces defined in spectralsequences.sty accomplishes this.
+
+\def\sseq@newstyles#1#2{\def\sseq@temppath{#1}\sseq@newstyles@#2,\sseq@nil}
+\def\sseq@newstyles@#1,{\sseq@newsseqstyle{\sseq@trimleadingspaces#1}\@ifnextchar\sseq@nil{\@gobble}{\sseq@newstyles@}}
+
+\def\sseq@set@newstyle#1{\pgfqkeys{/sseqpages}{#1}\pgfqkeys{/sseqpages/global}{#1}\pgfqkeys{/sseqpages/global/default}{#1}}
+
+% Here are two useful wrappers around addstylecodetotoks.
+
+% #1 -- token list
+% #2 -- path
+% #3 -- code
+\def\sseq@addstylecodetotoks#1#2#3{%
+ \let\sseq@savedoptioncode@store\sseq@savedoptioncode
+ \edef\sseq@savedoptioncode{\the#1}%
+ \sseq@addstylecodetooptions{#2}{#3}%
+ #1\@xp{\sseq@savedoptioncode}%
+ \let\sseq@savedoptioncode\sseq@savedoptioncode@store
+}
+
+% #1 -- macro
+% #2 -- path
+% #3 -- code
+\def\sseq@addstylecodetomacro#1#2#3{%
+ \let\sseq@savedoptioncode@store\sseq@savedoptioncode
+ \let\sseq@savedoptioncode#1%
+ \sseq@addstylecodetooptions{#2}{#3}%
+ \let#1\sseq@savedoptioncode
+ \let\sseq@savedoptioncode\sseq@savedoptioncode@store
+}
+
+\def\sseq@newsseqstyle#1{%
+ \sseq@setmacronospaces\sseq@tempcmd{sseq@#1style}%
+ \edef\sseq@tempcmdb{\csname\@xp\sseq@macroname\sseq@tempcmd @page\endcsname}%
+ \sseq@setmacronospaces\sseq@tempcmdc{sseq@the#1style}%
+ \@xp\edef\sseq@tempcmdc{\@nx\the\@xp\@nx\sseq@tempcmd\@nx\the\sseq@tempcmdb}%
+ \@xp\newtoks\sseq@tempcmd
+ \@xp\newtoks\sseq@tempcmdb
+ \sseq@x@addto@macro\sseq@stylelist{\sseq@tempcmd{\@nx\the\sseq@tempcmd}}%
+ \sseq@x@addto@macro\sseq@clearchangestyles{\sseq@tempcmdb{}}%
+ \sseq@set@newstyle{%
+ #1s/.ecode={%
+ \@nx\sseq@addstylecodetotoks{\sseq@tempcmd}{\sseq@temppath}{##1}
+ \@nx\ifsseq@inprogress
+ \@nx\sseq@savedpaths@xadd{\@nx\sseq@style@object{\sseq@tempcmd{\@nx\the\sseq@tempcmd}}}%
+ \@nx\fi
+ },%
+ change #1s/.ecode={%
+ \@nx\ifsseq@ispageenv\@nx\else\@nx\sseq@error@nn{change-style-data}{#1}{##1}\@nx\fi
+ \@nx\ifsseq@hasname\@nx\else\@nx\sseq@error@nn{change-style-standalone}{#1}{##1}\@nx\fi
+ \@nx\sseq@addstylecodetotoks{\sseq@tempcmdb}{\sseq@temppath}{##1}
+ }
+ }%
+}
+
+
+\newtoks\sseq@sseqstyle
+\newtoks\sseq@sseqstyle@page
+\def\sseq@thesseqstyle{\the\sseq@sseqstyle\the\sseq@sseqstyle@page}
+
+% sseqs has to be handled separately, because it must decide whether an option is a global option or not
+% and handle them at different times. This makes the code a little trickier.
+\def\sseq@sseqstyle@globalpart{}
+\sseq@g@addto@macro\sseq@stylelist{\sseq@sseqstyle{\the\sseq@sseqstyle}}%
+\sseq@g@addto@macro\sseq@clearchangestyles{\sseq@sseqstyle@page{}}%
+\sseq@set@newstyle{
+ sseqs/.code={%
+ % This will get called on each key that isn't a defined global option. Then we add it to \sseq@sseqstyle to be applied
+ % locally to absolutely everything.
+ \def\sseq@addstylecodetooptions@unknown{%
+ \let\sseq@sseqstyle@globalpart\sseq@savedoptioncode
+ \edef\sseq@savedoptioncode{\the\sseq@sseqstyle}%
+ \sseq@keys@unknown
+ \sseq@sseqstyle\@xp{\sseq@savedoptioncode}%
+ \let\sseq@savedoptioncode\sseq@sseqstyle@globalpart
+ }%
+ \sseq@addstylecodetomacro\sseq@sseqstyle@globalpart{/sseqpages/global}{#1}%
+ \ifsseq@inprogress
+ \sseq@savedpaths@xadd{\@nx\sseq@style@object{\sseq@sseqstyle{\the\sseq@sseqstyle}}}%
+ \fi
+ % Restore hook
+ \let\sseq@addstylecodetooptions@unknown\sseq@keys@unknown
+ },%
+ % It's not clear to me that "change sseqs" should even exist as an option.
+ change sseqs/.code={%
+ \ifsseq@ispageenv\else\sseq@error@nn{change-style-data}{sseq}{#1}\fi
+ \ifsseq@hasname\else\sseq@error@nn{change-style-standalone}{sseq}{#1}\fi
+ \sseq@addstylecodetotoks{\sseq@sseqstyle@page}{\sseq@temppath}{#1}%
+ }
+}
+
+
+% sseq needs special handling, so is done by hand above.
+%\sseq@newstyles{/sseqpages/}{sseq}% DON'T make this /global/default! Bad things will happen.
+\sseq@newstyles{/sseqpages/label,/sseqpages}{edge, label, edge label, differential label, struct line label}
+\sseq@newstyles{/sseqpages/class,/sseqpages}{class, permanent cycle, transient cycle, this page cycle}
+\sseq@newstyles{/sseqpages/differential,/sseqpages}{differential}
+\sseq@newstyles{/sseqpages/struct line,/sseqpages}{struct line, this page struct line}
+\sseq@newstyles{/sseqpages/class/label}{class label, inner class label, outer class label}
+\sseq@newstyles{/sseqpages/class/label/pin}{pin}
+\sseq@newstyles{/sseqpages/tikz primitives}{tikz primitive}
+\sseq@newstyles{/sseqpages/fit}{circle class}
+
+
+\sseq@set@newstyle{sseq/.style={sseqs={#1}},change sseq/.style={change sseqs={#1}},
+ classes/.style={classs={#1}},change classes/.style={change classs={#1}},
+ circle classes/.style={circle classs={#1}},change circle classes/.style={change circle classs={#1}},
+}% "class" has a nonstandard plural
+\sseq@set@newstyle{
+ cycles/.code={\pgfkeysalso{classes={#1}}},
+ permanent classes/.code={\pgfkeysalso{permanent cycles={#1}}},
+ transient classes/.code={\pgfkeysalso{transient cycles={#1}}},
+ this page classes/.code={\pgfkeysalso{this page cycles={#1}}},
+}%
+
+\def\sseq@newplotstyle#1{
+ \sseq@setmacronospaces\sseq@tempcmd{sseq@#1style}
+ \sseq@setmacronospaces\sseq@tempcmdb{sseq@#1style@default}
+ \sseq@eval{\@nx\sseq@addtostorelist\@xp\@nx\sseq@tempcmd{\@xp\@nx\sseq@tempcmdb}}
+ \@xp\def\sseq@tempcmdb{}
+ \pgfqkeys{/sseqpages/global}{%
+ #1 style/.ecode={
+ \@nx\sseq@addstylecodetomacro{\@xp\@nx\sseq@tempcmd}{/tikz}{##1}
+ }
+ }
+ % Only difference here is \sseq@tempcmdb -- which puts it in the default code -- instead of \sseq@tempcmd
+ \pgfqkeys{/sseqpages/global/default}{%
+ #1 style/.ecode={
+ \@nx\sseq@addstylecodetomacro{\@xp\@nx\sseq@tempcmdb}{/tikz}{##1}
+ }
+ }
+}
+
+
+\def\sseq@newplotstyles#1{\def\sseq@temppath{#1}\sseq@newplotstyles@#1,\sseq@nil}
+\def\sseq@newplotstyles@#1,{\sseq@newplotstyle{\sseq@trimleadingspaces#1}\@ifnextchar\sseq@nil{\@gobble}{\sseq@newplotstyles@}}
+\sseq@newplotstyles{x tick, y tick}
+
+
+%%
+%%
+%% Families (also called collections in code)
+%%
+%% These are supposed to make it easy to display / hide and change appearance of logically connected families of classes.
+%% In order to allow a set of options to be given that applies uniformly to all features, we separately apply the options in each of these bins.
+%%
+
+
+\def\sseq@featurelist{\\{class}\\{differential}\\{structline}\\{circleclass}\\{tikzprimitive}}
+\def\sseq@mapfeaturelist#1{\def\\##1{#1}\sseq@featurelist}
+
+\def\sseq@familylist{}
+\def\sseq@collections@featuretype{}
+\def\sseqnewfamily#1{%
+ % Make five bins associated to the new family, one for each feature type. Add them to storelist with empty defaults.
+ \sseq@mapfeaturelist{
+ \@xp\sseq@setmacronospaces\csname sseq@tempcmd@##1\endcsname{sseq@user@#1@##1@style}
+ \@xp\sseq@setmacronospaces\csname sseq@tempcmd@##1@default\endcsname{sseq@user@#1@##1@style@default}
+ \sseq@eval{\@nx\sseq@addtostorelist\@xptwo\@nx\csname sseq@tempcmd@##1\endcsname{\@xptwo\@nx\csname sseq@tempcmd@##1@default\endcsname}}
+ \@xptwo\def\csname sseq@user@#1@##1@style@default\endcsname{}
+ }
+ % This is for deciding what set of options to apply to a feature
+ \@xp\sseq@setnospaces\csname sseq@tempcmd\endcsname{sseq@user@#1}
+%
+ % A flag to decide whether to draw the family. Set/unset using "draw family" and "no family".
+ \@xp\sseq@addtostorelist\csname sseq@#1@maybedraw\endcsname{}
+ \makeatletter % the tikz people added an extra \scantokens into ecode...
+ \pgfqkeys{/sseqpages/global}{%
+ #1 style/.ecode={%
+ \@nx\sseq@addstylecodetomacro\@xp\@nx\sseq@tempcmd@class{/sseqpages/class,/sseqpages/collections/class}{##1}%
+ \@nx\sseq@addstylecodetomacro\@xp\@nx\sseq@tempcmd@differential{/sseqpages/differential,/sseqpages/collections/differential}{##1}%
+ \@nx\sseq@addstylecodetomacro\@xp\@nx\sseq@tempcmd@structline{/sseqpages/struct line,/sseqpages/collections/struct line}{##1}%
+ \@nx\sseq@addstylecodetomacro\@xp\@nx\sseq@tempcmd@circleclass{/sseqpages/fit,/sseqpages/collections/fit}{##1}%
+ \@nx\sseq@addstylecodetomacro\@xp\@nx\sseq@tempcmd@tikzprimitive{/sseqpages/tikz primitives,/sseqpages/collections/tikz primitives}{##1}%
+ },
+ draw #1/.ecode = {\def\@xp\@nx\csname sseq@#1@maybedraw\endcsname{\@nx\sseq@drawtrue}},
+ no #1/.ecode = {\def\@xp\@nx\csname sseq@#1@maybedraw\endcsname{\@nx\sseq@drawfalse}}
+ }%
+ \pgfqkeys{/sseqpages/global/default}{%
+ #1 style/.ecode={%
+ \@nx\sseq@addstylecodetomacro\@xp\@nx\sseq@tempcmd@class@default{/sseqpages/class}{##1}%
+ \@nx\sseq@addstylecodetomacro\@xp\@nx\sseq@tempcmd@differential@default{/sseqpages/differential}{##1}%
+ \@nx\sseq@addstylecodetomacro\@xp\@nx\sseq@tempcmd@structline@default{/sseqpages/struct line}{##1}%
+ \@nx\sseq@addstylecodetomacro\@xp\@nx\sseq@tempcmd@circleclass@default{/sseqpages/fit}{##1}%
+ \@nx\sseq@addstylecodetomacro\@xp\@nx\sseq@tempcmd@tikzprimitive@default{/sseqpages/tikz primitives}{##1}%
+ },
+ draw #1/.ecode = {\def\@xp\@nx\csname sseq@#1@maybedraw@default\endcsname{\@nx\sseq@drawtrue}},
+ no #1/.ecode = {\def\@xp\@nx\csname sseq@#1@maybedraw@default\endcsname{\@nx\sseq@drawfalse}}
+ }%
+%
+ \pgfqkeys{/sseqpages}{
+ #1/.ecode={
+ \@nx\sseq@d@addto@macro\@nx\sseq@familylist{\@nx\csname sseq@#1@maybedraw\endcsname}
+ \@nx\sseq@options@secondpass{
+ \unexpanded\@xp{\@xp\csname\sseq@tempcmd @\sseq@collections@featuretype @style\endcsname}
+ }
+ }
+ }
+}
+
+
+
+
+\def\sseq@collections@classlabels@hook{}
+\def\sseq@collections@innerclasslabels@hook{}
+\def\sseq@collections@outerclasslabels@hook{}
+\def\sseq@collections@pins@hook{}
+
+\def\sseq@collections@labels@hook{}
+\def\sseq@collections@edgelabels@hook{}
+\def\sseq@collections@differentiallabels@hook{}
+\def\sseq@collections@structlinelabels@hook{}
+
+\def\sseq@collections@setempty#1{\sseq@collections@setempty@#1,\sseq@nil}
+\def\sseq@collections@setempty@#1,{
+ \pgfqkeys{/sseqpages/collections}{
+ class/#1/.code={},
+ differential/#1/.code={},
+ struct line/#1/.code={},
+ fit/#1/.code={},
+ tikz primitives/#1/.code={}
+ }
+ \@ifnextchar\sseq@nil{\@gobble}{\sseq@collections@setempty@}
+}
+
+\sseq@collections@setempty{%
+ classes, cycles, permanent classes, permanent cycles, transient classes, transient cycles, this page classes, this page cycles,
+ edges, differentials,struct lines, this page struct lines,circleclasses, tikz primitives,
+ labels, edge labels, differential labels, struct line labels,
+ class labels, inner class labels, outer class labels,pins
+}
+
+% #1 -- path
+% #2 -- hook name
+% #3 -- code
+\def\sseq@collections@addtohook#1#2#3{
+ \sseq@keys@do{
+ \let\sseq@savedoptioncode@store\sseq@savedoptioncode\let\sseq@savedoptioncode\empty
+ \pgfqkeys{#1}{#3}
+ \edef\sseq@savedoptioncode{\unexpanded\@xp{\sseq@savedoptioncode@store}\def\@nx#2{\unexpanded\@xp{\sseq@savedoptioncode}}}
+ }
+}
+
+\pgfqkeys{/sseqpages/collections}{
+ class/classes/.style={/sseqpages/class/.cd,#1},
+ class/cycles/.style={/sseqpages/class/.cd,#1},
+ class/permanent classes/.code={\ifsseq@permanentcycle\pgfkeysalso{/sseqpages/class/.cd,#1}\fi},
+ class/permanent cycles/.code={\ifsseq@permanentcycle\pgfkeysalso{/sseqpages/class/.cd,#1}\fi},
+ class/transient classes/.code={\ifsseq@permanentcycle\else\pgfkeysalso{/sseqpages/class/.cd,#1}\fi},
+ class/transient cycles/.code={\ifsseq@permanentcycle\else\pgfkeysalso{/sseqpages/class/.cd,#1}\fi},
+ class/this page classes/.code={\ifsseq@thispage\pgfkeysalso{/sseqpages/class/.cd,#1}\fi},
+ class/this page cycles/.code={\ifsseq@thispage\pgfkeysalso{/sseqpages/class/.cd,#1}\fi},
+ differential/edges/.style={/sseqpages/differential/.cd,#1},
+ differential/differentials/.style={/sseqpages/differential/.cd,#1},
+ struct line/edges/.style={/sseqpages/struct line/.cd,#1},
+ struct line/struct lines/.style={/sseqpages/struct line/.cd,#1},
+ struct line/this page struct lines/.code={\ifsseq@thispage\pgfkeysalso{/sseqpages/struct line/.cd,#1}\fi},
+ fit/circleclasses/.style={/sseqpages/fit/.cd,#1},
+ tikz primitives/tikz primitives/.style={/sseqpages/tikz primitives/.cd,#1},
+% labels
+ class/class labels/.code/.expanded={\unexpanded\@xp{\sseq@collections@addtohook{/sseqpages/class/label}{\sseq@collections@classlabels@hook}{#1}}},
+ class/inner class labels/.code/.expanded={\unexpanded\@xp{\sseq@collections@addtohook{/sseqpages/class/label}{\sseq@collections@innerclasslabels@hook}{#1}}},
+ class/outer class labels/.code/.expanded={\unexpanded\@xp{\sseq@collections@addtohook{/sseqpages/class/label}{\sseq@collections@outerclasslabels@hook}{#1}}},
+ class/pins/.code/.expanded={\unexpanded\@xp{\sseq@collections@addtohook{/sseqpages/class/label/pin}{\sseq@collections@pins@hook}{#1}}},
+%
+ class/labels/.code/.expanded={\unexpanded\@xp{\sseq@collections@addtohook{/sseqpages/label}{\sseq@collections@labels@hook}{#1}}},
+ differential/labels/.code/.expanded={\unexpanded\@xp{\sseq@collections@addtohook{/sseqpages/label}{\sseq@collections@labels@hook}{#1}}},
+ struct line/labels/.code/.expanded={\unexpanded\@xp{\sseq@collections@addtohook{/sseqpages/label}{\sseq@collections@labels@hook}{#1}}},
+%
+ differential/edge labels/.code/.expanded=
+ {\unexpanded\@xp{\sseq@collections@addtohook{/sseqpages/label}{\sseq@collections@edgelabels@hook}{#1}}},
+ struct line/edge labels/.code/.expanded=
+ {\unexpanded\@xp{\sseq@collections@addtohook{/sseqpages/label}{\sseq@collections@edgelabels@hook}{#1}}},
+ differential/differential labels/.code/.expanded=
+ {\unexpanded\@xp{\sseq@collections@addtohook{/sseqpages/label}{\sseq@collections@differentiallabels@hook}{#1}}},
+ struct line/struct line labels/.code/.expanded=
+ {\unexpanded\@xp{\sseq@collections@addtohook{/sseqpages/label}{\sseq@collections@structlinelabels@hook}{#1}}},
+}
+
+
+
+
+%%% Global keys handlers and infrastructure
+
+\def\sseq@keys@lastpart{%
+ \edef\pgfkeyscurrentkey{\pgfkeyscurrentpath}%
+ \pgfkeys@split@path
+ \edef\sseq@temp{\pgfkeyscurrentname}%
+ \edef\sseq@temp@ii{\pgfkeyscurrentkey}%
+}
+
+\def\sseq@sanitize@intexpr#1#2{
+ \sseq@ifintexpr{#1}{
+ \edef\sseq@sanitize@output{\the\numexpr#1}
+ }{
+ \sseq@error@xx{invalid-intexpr}{\unexpanded{#2}}{#1}
+ \sseq@break
+ }
+}
+
+\def\sseq@sanitize@positiveintexpr#1#2{
+ \sseq@sanitize@intexpr{#1}{#2}
+ \ifnum\sseq@sanitize@output<1\relax
+ \sseq@error@xx{invalid-positive-intexpr}{\unexpanded{#2}}{#1}
+ \@xp\sseq@break
+ \fi
+}
+
+\def\sseq@sanitize@dimenexpr#1#2{
+ \sseq@ifpgfmathexpr{#1}{
+ \ifpgfmathunitsdeclared\else
+ \sseq@error@xx{invalid-dimenexpr}{\unexpanded{#2}}{#1}
+ \@xp\sseq@break
+ \fi
+ \edef\sseq@sanitize@output{\sseq@mathresult}
+ }{
+ \sseq@error@xx{invalid-dimenexpr}{\unexpanded{#2}}{#1}
+ \sseq@break
+ }
+}
+
+\def\sseq@sanitize@color#1#2{
+ \begingroup
+ \global\sseq@gtempiffalse
+ \def\c@lor@error##1{\global\sseq@gtempiftrue}
+ \color{#1}
+ \endgroup
+ \ifsseq@gtempif
+ \sseq@error@xx{invalid-color}{\unexpanded{#2}}{#1}
+ \@xp\sseq@break
+ \else
+ \def\sseq@sanitize@output{#1}
+ \fi
+}
+
+\pgfqkeys{/handlers}{
+ .sseq store in/.code 2 args={%
+ \sseq@addtostorelist#1{#2}%
+ \sseq@keys@lastpart
+ \pgfkeys{%
+ \sseq@temp@ii/.store in=#1,%
+ /sseqpages/global/default/\sseq@temp/.store in/.expand once=\csname \sseq@macroname#1@default\endcsname
+ }%
+ },
+ .sseq store in sanitize/.code n args={3}{%
+ \sseq@addtostorelist#1{#2}%
+ \sseq@keys@lastpart
+ \pgfkeys{%
+ \sseq@temp@ii/.code/.expanded={%
+ \unexpanded{#3{##1}}{\sseq@temp=\unexpanded{##1}}%
+ \unexpanded{\edef#1{\unexpanded\@xp{\sseq@sanitize@output}}}%
+ \sseq@breakpoint
+ },
+ \sseq@temp@ii/.value required,
+ /sseqpages/global/default/\sseq@temp/.code/.expanded={%
+ \unexpanded{#3{##1}}{\sseq@temp=\unexpanded{##1}}%
+ \edef\@xp\@nx\csname \sseq@macroname#1@default\endcsname\unexpanded{{\unexpanded\@xp{\sseq@sanitize@output}}}%
+ \sseq@breakpoint
+ },
+ /sseqpages/global/default/\sseq@temp/.value required
+ }%
+
+ },
+ .sseq store int in/.code 2 args={
+ \sseq@keys@lastpart
+ \pgfkeysalso{%
+ \sseq@temp@ii/.sseq store in sanitize={#1}{#2}\sseq@sanitize@intexpr
+ }
+ },
+ .sseq store positive int in/.code 2 args={
+ \sseq@keys@lastpart
+ \pgfkeysalso{%
+ \sseq@temp@ii/.sseq store in sanitize={#1}{#2}\sseq@sanitize@positiveintexpr
+ }
+ },
+ .sseq store dimen in/.code 2 args={
+ \sseq@keys@lastpart
+ \pgfkeysalso{%
+ \sseq@temp@ii/.sseq store in sanitize={#1}{#2}\sseq@sanitize@dimenexpr
+ }
+ },
+ .sseq append to style/.code 2 args={%
+ \sseq@addkeytostorelist{#1}{\pgfkeysalso{#2}}%
+ \sseq@keys@lastpart
+ \pgfkeys{%
+ \sseq@temp@ii/.style={#1/.append style={##1}},%
+ /sseqpages/global/default/\sseq@temp/.ecode={%
+ \@nx\apptocmd\@xp\@nx\csname pgfk@#1/.@cmd@default\endcsname{\@nx\pgfkeysalso{##1}}{}{\@nx\sseq@error{this-shouldnt-happen}}%
+ \global\let\@xp\@nx\csname pgfk@#1/.@cmd@default\endcsname\@xp\@nx\csname pgfk@#1/.@cmd@default\endcsname
+ }%
+ }%
+ },
+ .sseq is if/.code={%
+ \sseq@addiftostorelist@truedefault{#1}%
+ \sseq@keys@lastpart
+ \pgfkeys{%
+ \sseq@temp@ii/.is if=sseq@#1,%
+ /sseqpages/global/default/\sseq@temp/.code={\@xp\edef\csname sseq@#1@default\endcsname{\global\@xp\@nx\csname sseq@#1##1\endcsname}},%
+ /sseqpages/global/default/\sseq@temp/.default=true,%
+ }%
+ },
+ .sseq is if false/.code={%
+ \sseq@keys@lastpart
+ \pgfkeys{%
+ /sseqpages/global/\sseq@temp/.code/.expanded={\@xp\@nx\csname sseq@#1false\endcsname},
+ /sseqpages/global/\sseq@temp/.value forbidden,
+ % value forbidden and ecode don't work correctly together so we have to use .code/.expanded .
+ % I made a thread here: https://sourceforge.net/p/pgf/discussion/477362/thread/3e75d813/
+ /sseqpages/global/default/\sseq@temp/.code/.expanded={\def\@xp\@nx\csname sseq@#1@default\endcsname{\global\@xp\@nx\csname sseq@#1false\endcsname}},
+ /sseqpages/global/default/\sseq@temp/.value forbidden,
+ }%
+ },
+ .sseq default code/.code={%
+ \sseq@keys@lastpart
+ \pgfkeys{/sseqpages/global/default/\sseq@temp/.code={#1}}%
+ },
+ .sseq default code 2 args/.code={%
+ \sseq@keys@lastpart
+ \pgfkeys{/sseqpages/global/default/\sseq@temp/.code 2 args={#1}}%
+ },
+ .sseq default error/.code={%
+ \sseq@keys@lastpart
+ \pgfkeys{/sseqpages/global/default/\sseq@temp/.ecode={\@nx\sseq@error@n{option-no-default-allowed}{\sseq@temp}}}%
+ },
+ .sseq x y alias/.code={%
+ \sseq@keys@lastpart
+ \ifx\pgfkeysnovalue#1%
+ \edef\sseq@tempb##1{##1 \sseq@temp}%
+ \else
+ \def\sseq@tempb##1{#1}%
+ \sseq@usedparam\sseq@tempb
+ \fi
+ \pgfkeys{%
+ /sseqpages/global/\sseq@temp/.code/.expanded={\@nx\pgfkeysalso{\sseq@tempb{x}=\unexpanded{{##1}},\sseq@tempb{y}=\unexpanded{{##1}}}},%
+ /sseqpages/global/default/\sseq@temp/.code/.expanded={\@nx\pgfkeysalso{\sseq@tempb{x}=\unexpanded{{##1}},\sseq@tempb{y}=\unexpanded{{##1}}}}%
+ }%
+ }
+}
+
+\bgroup\lccode`*=`#\lowercase{\egroup
+ \def\sseq@usedparam#1{
+ \@xp\sseq@usedparam@\@xp#1\meaning#1*\@nil
+ }
+ \def\sseq@usedparam@#1#2->#3*#4\@nil{
+ \sseq@ifempty{#4}{
+ \@xp\def\@xp#1\@xp##\@xp1\@xp{\@xp##\@xp1\@xp\space#1{}}
+ }{}
+ }
+}
+
+\def\sseq@patchrepeat#1#2#3{%
+ \patchcmd#1{#2}{#3}{\sseq@patchrepeat{#1}{#2}{#3}}{}%
+}
+
+\def\sseq@stylehandler@processpath#1#2{%
+ \def\sseq@savedoptioncode{}%
+ \sseq@eval{\@nx\sseq@addstylecodetooptions{/sseqpages/#1}{\unexpanded\@xp{\sseq@keybody{*1}}}}%
+ \edef\sseq@savedoptioncode##1{\unexpanded\@xp{\sseq@savedoptioncode}}%
+ \sseq@patchrepeat\sseq@savedoptioncode{*1}{##1}%
+ \sseq@eval{\@nx\pgfqkeys{/sseqpages/#1}{\sseq@temp/.#2={\unexpanded\@xp{\sseq@savedoptioncode{##1}}}}}%
+}
+
+\def\sseq@add@tikzkey@to@options@forstylehander#1{\sseq@e@addto@options{#1}}
+\pgfqkeys{/handlers}{ % defines sseq style, etc
+ sseq @@ make style handler/.style 2 args={%
+ .sseq #1/.code={%
+ \def\sseq@keybody####1{##1}%
+ \sseq@keys@lastpart
+ \let\sseq@savedoptioncode@store\sseq@savedoptioncode
+ \def\sseq@keys@unknown@error{}%
+ \let\sseq@add@tikzkey@to@options\sseq@add@tikzkey@to@options@forstylehander
+%
+ \sseq@stylehandler@processpath{scope}{#2}%
+ \sseq@stylehandler@processpath{class}{#2}%
+ \sseq@stylehandler@processpath{differential}{#2}%
+ \sseq@stylehandler@processpath{struct line}{#2}%
+ \sseq@stylehandler@processpath{circle classes}{#2}%
+ \sseq@stylehandler@processpath{tikz primitives}{#2}%
+%
+ \let\sseq@savedoptioncode\sseq@savedoptioncode@store
+ \let\sseq@addstylecodetooptions@unknown\sseq@keys@unknown
+ \let\sseq@keys@unknown@error\sseq@keys@unknown@error@normal
+ \let\sseq@add@tikzkey@to@options\sseq@e@addto@options
+ }%
+ },%
+ sseq @@ make code handler/.style={%
+ .sseq #1/.code={%
+ \sseq@keys@lastpart
+ \pgfkeys{
+ /sseqpages/class/\sseq@temp/.#1={##1},%
+ /sseqpages/differential/\sseq@temp/.#1={##1},%
+ /sseqpages/struct line/\sseq@temp/.#1={##1}%
+ }%
+ }%
+ },
+%
+ sseq @@ make style handler={style}{code},
+ sseq @@ make style handler={prefix style}{prefix code},
+ sseq @@ make style handler={append style}{append code},
+ sseq @@ make code handler=code,
+ sseq @@ make code handler=prefix code,
+ sseq @@ make code handler=append code,
+% these just are the same as /.style, etc.
+ .global sseq style/.code={\pgfkeys{\pgfkeyscurrentpath/.code=\pgfkeysalso{#1}}},
+ .global sseq prefix style/.code={\pgfkeys{\pgfkeyscurrentpath/.add code={\pgfkeysalso{#1}}{}}},
+ .global sseq append style/.code={\pgfkeys{\pgfkeyscurrentpath/.add code={}{\pgfkeysalso{#1}}}},
+%
+%
+ .sseq grading/.code 2 args={%
+ \sseq@keys@lastpart
+ \pgfkeys{%
+ /sseqpages/global/\sseq@temp/.estyle={\unexpanded{degree={#1}{#2}}},%I'm not really sure why these need to be estyle and \unexpanded but it does...
+ /sseqpages/global/default/\sseq@temp/.estyle={\unexpanded{degree={#1}{#2}}},%
+ /sseqpages/\sseq@temp/.sseq @ global only
+ }%
+ },
+ Adams grading/.sseq grading={-1}{#1},
+ homological Serre grading/.sseq grading={-#1}{#1-1},
+ cohomological Serre grading/.sseq grading={#1}{1-#1},
+}
+
+%%% Define global keys setter
+% Most keys are run twice -- once in the "/sseqpages/global" directory and once in the "/sseqpages/global/default" directory
+% The default "default" behavior is to add the option to the "sseq" style.
+% Keys prefixed by a tilde or using an "ignored" handler are only run in the "/sseqpages/global directory".
+\gdef\sseq@globalkeys#1{%
+ \let\sseq@storetilde~%
+ \def~{}%
+ \pgfqkeys{/sseqpages/global}{#1}% run keys in global directory
+ \sseq@globalkeys@#1,\sseq@nil % handle defaults
+ \let~\sseq@storetilde
+}
+
+\def\sseq@globalkeys@#1,{%
+ \sseq@tempiftrue
+ \sseq@keys@removehandler{#1}% this sets \sseq@tempif to be false if it has an ignored handler
+ % It also sets \sseq@temp to be the key.
+ \ifsseq@tempif % If not ignored handler
+ \pgfkeys{/sseqpages/\sseq@temp/.sseq @ global only}%
+ % Check if no previous default behavior is defined, and if not, define it to pass key to sseqs.
+ \pgfkeysifdefined{/sseqpages/global/default/\sseq@temp/.@cmd}{}{%
+ \pgfkeys{/sseqpages/global/default/\sseq@temp/.ecode={\@nx\pgfkeysalso{sseqs={\sseq@temp}}}}%
+ }%
+ \fi
+ \sseq@globalkeys@next
+}
+
+\def\sseq@globalkeys@eatentry#1,{%
+ \sseq@globalkeys@next
+}
+
+% iterate over keys, throwing away empty keys and ones that start with ~.
+\def\sseq@globalkeys@next{%
+ \@ifnextchar\sseq@nil{\@gobble}{%
+ \@ifnextchar~{\sseq@globalkeys@eatentry}{%
+ \@ifnextchar,{\sseq@globalkeys@eatentry}{%
+ \sseq@globalkeys@
+ }%
+ }%
+ }%
+}
+
+% Handlers to ignore for
+\def\sseq@keys@ignorehandlers{%
+ \\{value forbidden}\\{value required}\\{default}%\\{sseq store in}\\{sseq is if}%
+ \\{sseq default code}\\{sseq default code 2 args}%
+}
+\def\sseq@keys@removehandler#1{\sseq@keys@removehandler@#1=\sseq@nil}
+\def\sseq@keys@removehandler@#1/.#2=#3\sseq@nil{%
+ \bgroup
+ \def\sseq@temp{#2}%
+ \def\\##1{\def\sseq@tempii{##1}\ifx\sseq@tempii\sseq@temp \global\sseq@tempiffalse\let\\\@gobble\fi}%
+ \sseq@keys@ignorehandlers
+ \egroup
+ \def\sseq@temp{#1}%
+}
+
+
+%%% Define global keys
+\def\sseq@currentoptionpath{default}
+\pgfkeys{
+ % \sseq@currentoptionpath is set to \sseq@thename in \sseqdata, then "temp" in \sseqpage
+ /sseqpages/global/.unknown/.code={%
+ \let\sseq@tempa\pgfkeyscurrentname
+ \let\sseq@tempb\pgfkeyscurrentvalue
+ \sseq@eval{\@nx\pgfqkeys{/sseqpages/global}{/sseqpages/userstyles/\sseq@tempa/.try={\unexpanded\@xp{\sseq@tempb}}}}
+ \ifpgfkeyssuccess\else
+ \pgfkeysifdefined{/tikz/\pgfkeyscurrentname/.@cmd}{\@firstofone}{\pgfkeysifdefined{/tikz/\pgfkeyscurrentname}{\@firstofone}{%
+ \sseq@error{unknown-global}\@gobble
+ }}{%
+ \pgfkeysalso{{\sseq@currentoptionpath} options/.append code/.expand twice={\@xp\@xp\@xp\tikzset\@xp\@xp\@xp{\@xp\sseq@tempa\@xp=\@xp{\sseq@tempb}}}}%
+ \sseq@eval{%
+ \global\let\@xp\@nx\csname pgfk@/sseqpages/global/{\sseq@currentoptionpath} options/.@cmd\endcsname
+ \@xp\@nx\csname pgfk@/sseqpages/global/{\sseq@currentoptionpath} options/.@cmd\endcsname
+ }%
+ }%
+ \fi
+ },
+ /sseqpages/global/default/.unknown/.code={%
+ \sseq@passoptionto{/sseqpages/global}%
+ },
+ /sseqpages/global/{default} options/.code={},
+ /sseqpages/global/name only/.unknown/.code={},
+ /sseqpages/global/sseqstyle/.unknown/.code={}
+}
+
+\pgfqkeys{/sseqpages/global/name only}{
+ name/.code={\def\sseq@thename{#1}\sseq@hasnametrue},
+ name/.value required,
+ keep changes/.is if=sseq@keepchanges,
+ update existing/.code={\ifsseq@ispageenv\sseq@error{page-update-existing}\fi\sseq@updateexistingtrue},
+ update existing/.sseq default code={\sseq@updateexistingtrue}
+}
+
+\def\sseq@printnum@inftytosymbol#1{%
+ \ifnum#1=\sseq@infinity\relax
+ $\infty$%
+ \else
+ #1%
+ \fi
+}
+
+
+\sseq@addtostorelist\sseq@grid{\relax}
+\sseq@addiftostorelist{hasdegree}
+\let\sseq@maybemathswitch\relax
+\sseq@globalkeys{%
+ name/.code={},
+ name/.sseq default error,
+ ~page/.code={
+ \ifsseq@hasname
+ \sseq@getnumrange\sseq@thepage{#1}%
+ \sseq@thepagecount=\sseq@thepage\relax
+ \let\page\sseq@thepage
+ \ifnum\sseq@thepage=\sseq@infinity
+ \def\page{$\infty$}%
+ \fi
+ \def\sseq@temp{\sseq@novalue}
+ \ifx\sseq@printpageas\sseq@temp
+ \def\sseq@printpageas{Page \page}
+ \ifnum\sseq@thepage<\sseq@thepagemax
+ \ifnum\sseq@thepagemax=\sseq@infinity\relax
+ \def\sseq@printpageas{Page \page\space(all differentials)}
+ \else
+ \def\sseq@printpageas{Page \page\space(differentials of length $\leq\sseq@thepagemax$)}
+ \fi
+ \fi
+ \fi
+ \edef\sseq@thepagemm{\the\numexpr\sseq@thepagecount-1\relax}
+ \ifnum\sseq@thepagemax=\z@
+ \let\sseq@thepagemaxpp\sseq@infinity
+ \else
+ \edef\sseq@thepagemaxpp{\the\numexpr\sseq@thepagemax+1\relax}
+ \fi
+ \else
+ \sseq@error{page-no-name}
+ \fi
+ },
+ ~page/.sseq default error,% implement page default?
+ degree/.code 2 args={\def\sseq@targetx##1{#1}\def\sseq@targety##1{#2}},
+ degree/.sseq default code 2 args={\def\sseq@targetx@default##1{#1}\def\sseq@targety@default##1{#2}},
+ strict degree/.sseq is if=strictdegree,
+ lax degree/.sseq is if false=strictdegree,
+ update existing/.code={}, % processed with "name" key and "keep changes" key in /sseqpages/global/name only
+ keep changes/.code={%
+ \ifsseq@hasname\else\sseq@error{keep-changes-no-name}\fi
+ \ifsseq@ispageenv\else\sseq@error{keep-changes-sseqdata}\fi
+ },
+ grid/.code={\ifcsname sseq@grid@#1\endcsname\@xp\let\@xp\sseq@grid\csname sseq@grid@#1\endcsname\else\sseq@error@n{unknown-grid}{#1}\fi},
+ grid/.sseq default code={\ifcsname sseq@grid@#1\endcsname\@xp\let\@xp\sseq@grid@default\csname sseq@grid@#1\endcsname\else\sseq@error@n{unknown-grid}{#1}\fi},
+ grid color/.sseq store in sanitize=\sseq@gridcolor{gray}\sseq@sanitize@color,
+ grid step/.sseq x y alias,
+ grid step/.value required,
+ x grid step/.sseq store positive int in=\sseq@xgridstep{1},
+ y grid step/.sseq store positive int in=\sseq@ygridstep{1},
+ math nodes/.sseq is if=mathnodes, % TODO: make this locally toggleable
+ custom clip/.sseq store in=\sseq@customclip{},
+ clip/.sseq is if=clip,
+ draw differentials/.sseq is if=drawdifferentials,
+ no differentials/.sseq is if false={drawdifferentials},
+ draw struct lines/.sseq is if=drawstructlines,
+ no struct lines/.sseq is if false={drawstructlines},
+ draw orphan edges/.sseq is if=draworphanedges,
+ no orphan edges/.sseq is if false={draworphanedges},
+ class pattern/.sseq store in=\sseq@classpattern{standard},
+ class pattern/.add code={\ifcsname sseq@#1xoffset1/1\endcsname\else\sseq@error{unknown-class-pattern}\@xp\sseq@break\fi}{\sseq@breakpoint},
+ class placement transform/.code={%
+ \bgroup
+ \let\tikz@transform\pgfutil@empty
+ \pgfqkeys{/sseqpages/class placement}{#1}%
+ \global\let\sseq@classplacementtransform\tikz@transform
+ \egroup
+ },
+ class placement transform/.sseq default code={%
+ \bgroup
+ \let\tikz@transform\pgfutil@empty
+ \pgfqkeys{/sseqpages/class placement}{#1}%
+ \global\let\sseq@classplacementtransform@default\tikz@transform
+ \egroup
+ },
+ class placement transform/.value required,
+ add class placement transform/.code={%
+ \bgroup
+ \let\tikz@transform\pgfutil@empty
+ \pgfqkeys{/sseqpages/class placement}{#1}%
+ \sseq@x@addto@macro\sseq@classplacementtransform{\unexpanded\@xp\tikz@transform}
+ \egroup
+ },
+ add class placement transform/.sseq default code={%
+ \bgroup
+ \let\tikz@transform\pgfutil@empty
+ \pgfqkeys{/sseqpages/class placement}{#1}%
+ \sseq@x@addto@macro\sseq@classplacementtransform@default{\unexpanded\@xp\tikz@transform}
+ \egroup
+ },
+ add class placement transform/.value required,
+}
+
+
+\sseq@addiftostorelist@truedefault{rangecheck}
+\sseq@addiftostorelist{rangecheck@sideways}
+\sseq@globalkeys{%
+ range check standard/.code={\sseq@rangechecktrue\sseq@rangecheck@sidewaysfalse},
+ range check standard/.sseq default code={%
+ \def\sseq@rangecheck@default{\global\sseq@rangechecktrue}%
+ \def\sseq@rangecheck@sideways@default{\global\sseq@rangecheck@sidewaysfalse}%
+ },
+ range check sideways/.code=rangecheck@sideways,
+ range check sideways/.sseq default code={\def\sseq@rangecheck@sideways@default{\global\sseq@rangecheck@sidewaystrue}},
+ range check off/.code={\sseq@rangecheckfalse},
+ range check off/.sseq default code={\def\sseq@rangecheck@default{\global\sseq@rangecheckfalse}},
+}
+
+\sseq@mathnodestrue
+
+\sseq@addtostorelist\sseq@targetx{}\sseq@addtostorelist\sseq@targety{}
+\sseq@addtostorelist\sseq@xtickstep{1}\sseq@addtostorelist\sseq@ytickstep{1}
+\sseq@addtostorelist\sseq@classplacementtransform{}
+
+
+
+%%% Range keys and commands
+\sseq@globalkeys{%
+ x range/.code 2 args={\sseq@xrange{#1}{#2}},
+ x range/.sseq default code 2 args={\sseq@xrange@default{#1}{#2}},
+ y range/.code 2 args={\sseq@yrange{#1}{#2}},
+ y range/.sseq default code 2 args={\sseq@yrange@default{#1}{#2}},
+ xrange/.forward to=/sseqpages/global/x range,
+ yrange/.forward to=/sseqpages/global/y range
+}
+
+% These are set up so that if the user specifies no \xrange, the minimum position of any node can be calculated on the fly.
+\@xp\sseq@addtostorelist\@xp\sseq@xmin\@xp{\sseq@infinity}
+\@xp\sseq@addtostorelist\@xp\sseq@ymin\@xp{\sseq@infinity}
+\@xp\sseq@addtostorelist\@xp\sseq@xmax\@xp{\@xp-\sseq@infinity}
+\@xp\sseq@addtostorelist\@xp\sseq@ymax\@xp{\@xp-\sseq@infinity}
+\sseq@addtostorelist\xmin{\protect\sseq@xmin}
+\sseq@addtostorelist\ymin{\protect\sseq@ymin}
+\sseq@addtostorelist\xmax{\protect\sseq@xmax}
+\sseq@addtostorelist\ymax{\protect\sseq@ymax}
+% These are used to decide whether to add a certain node to savedpaths.
+% In general the check is whether xmin<=xcoord<=xmax, but since Latex has no <=, set xminmm=xmin-1, xmaxpp=xmax+1.
+% If the user doesn't specify a range, all nodes should be rendered
+% To ensure that xminmm<xccord<xminpp in that case, set the default to xminmm=-\infty, xmaxpp=+\infty.
+\@xp\sseq@addtostorelist\@xp\sseq@xminmm\@xp{\@xp-\sseq@infinity}
+\@xp\sseq@addtostorelist\@xp\sseq@yminmm\@xp{\@xp-\sseq@infinity}
+\@xp\sseq@addtostorelist\@xp\sseq@xmaxpp\@xp{\sseq@infinity}
+\@xp\sseq@addtostorelist\@xp\sseq@ymaxpp\@xp{\sseq@infinity}
+
+\sseq@addiftostorelist{specifiedxrange}
+\sseq@addiftostorelist{specifiedyrange}
+% We used to \let\xmin\sseq@xmin in here, but that's a bad idea. Hopefully things work now...
+\def\sseq@xrange#1#2{%
+ \global\let\sseq@xminmax\@gobbletwo % Don't need to calculate range of nodes if user specifies it
+ \global\sseq@specifiedxrangetrue
+ \sseq@sanitize@intexpr{#1}{x range={#1}{#2}}
+ \sseq@sanitize@intexpr{#2}{x range={#1}{#2}}
+ \xdef\sseq@xmin{\the\numexpr#1}%
+ \xdef\sseq@xmax{\the\numexpr#2}%
+ % Need to know decremented \xmin and incremented \xmax to decide which nodes to print
+ \sseq@tempcount=#1 \advance\sseq@tempcount-1\xdef\sseq@xminmm{\the\sseq@tempcount}%
+ \sseq@tempcount=#2 \advance\sseq@tempcount1\xdef\sseq@xmaxpp{\the\sseq@tempcount}%
+ \sseq@breakpoint
+}
+
+\def\sseq@yrange#1#2{%
+ \global\let\sseq@yminmax\@gobbletwo %
+ \sseq@sanitize@intexpr{#1}{y range={#1}{#2}}
+ \sseq@sanitize@intexpr{#2}{y range={#1}{#2}}
+ \global\sseq@specifiedyrangetrue
+ \xdef\sseq@ymin{\the\numexpr#1}%
+ \xdef\sseq@ymax{\the\numexpr#2}%
+ \sseq@tempcount=#1 \advance\sseq@tempcount-1\xdef\sseq@yminmm{\the\sseq@tempcount}%
+ \sseq@tempcount=#2 \advance\sseq@tempcount1\xdef\sseq@ymaxpp{\the\sseq@tempcount}%
+}
+
+\def\sseq@xrange@default#1#2{%
+ \global\let\sseq@xminmax\@gobbletwo % Don't need to calculate range of nodes if user specifies it
+ \gdef\sseq@xmin@default{#1}%
+ \gdef\sseq@xmax@default{#2}%
+ % Need to know decremented \xmin and incremented \xmax to decide which nodes to print
+ \sseq@tempcount=#1 \advance\sseq@tempcount-1\xdef\sseq@xminmm@default{\the\sseq@tempcount}%
+ \sseq@tempcount=#2 \advance\sseq@tempcount1\xdef\sseq@xmaxpp@default{\the\sseq@tempcount}%
+}
+\def\sseq@yrange@default#1#2{%
+ \global\let\sseq@yminmax\@gobbletwo %
+ \gdef\sseq@ymin@default{#1}%
+ \gdef\sseq@ymax@default{#2}%
+ \sseq@tempcount=#1 \advance\sseq@tempcount-1\xdef\sseq@yminmm@default{\the\sseq@tempcount}%
+ \sseq@tempcount=#2 \advance\sseq@tempcount1\xdef\sseq@ymaxpp@default{\the\sseq@tempcount}%
+}
+
+% If #1 is less than \xmin or greater than \xmax, update \xmin or \xmax appropriately
+% Gets set to \@gobble if user specifies range
+\def\sseq@xminmax@active#1#2{%
+ \ifnum#1<\sseq@xmin\relax
+ \ifx\sseq@yminmax\@gobbletwo
+ \ifnum#2>\sseq@yminmm\relax
+ \ifnum#2<\sseq@ymaxpp\relax
+ \xdef\sseq@xmin{#1}%
+ \fi
+ \fi
+ \else
+ \xdef\sseq@xmin{#1}%
+ \fi
+ \fi
+ \ifnum#1>\sseq@xmax\relax
+ \ifx\sseq@yminmax\@gobbletwo
+ \ifnum#2>\sseq@yminmm\relax
+ \ifnum#2<\sseq@ymaxpp\relax
+ \xdef\sseq@xmax{#1}%
+ \fi
+ \fi
+ \else
+ \xdef\sseq@xmax{#1}%
+ \fi
+ \fi
+}
+
+\def\sseq@yminmax@active#1#2{%
+ \ifnum#2<\sseq@ymin\relax
+ \ifx\sseq@xminmax\@gobbletwo
+ \ifnum#1>\sseq@xminmm\relax
+ \ifnum#1<\sseq@xmaxpp\relax
+ \xdef\sseq@ymin{#2}%
+ \fi
+ \fi
+ \else
+ \xdef\sseq@ymin{#2}%
+ \fi
+ \fi
+ \ifnum#2>\sseq@ymax\relax
+ \ifx\sseq@xminmax\@gobbletwo
+ \ifnum#1>\sseq@xminmm\relax
+ \ifnum#1<\sseq@xmaxpp\relax
+ \xdef\sseq@ymax{#2}%
+ \fi
+ \fi
+ \else
+ \xdef\sseq@ymax{#2}%
+ \fi
+ \fi
+}
+
+%%% Axes and Layout options
+\sseq@addtostorelist{\sseq@xtickfn}{} % These should initially be the identity macro, but addtostorelist doesn't accept functions as default
+\sseq@addtostorelist{\sseq@ytickfn}{} % so the default is instated in begin{sseqdata} / begin{sseqpicture}.
+
+\let\sseq@printpageas\relax
+\sseq@globalkeys{%
+ title/.sseq store in=\sseq@title{},
+ title style/.sseq append to style={/sseqpages/global/every title}{at={(0.5*\xmin+0.5*\xmax,\ymax)}},
+ draw title/.sseq is if=drawtitle,
+ print page as/.sseq store in=\sseq@printpageas{\sseq@novalue},
+ no title/.code=\sseq@drawtitlefalse,
+%
+ x label/.sseq store in=\sseq@xaxislabel{},
+ y label/.sseq store in=\sseq@yaxislabel{},
+ x label style/.sseq append to style={/sseqpages/global/every x axis label}{at={(0.5*\xmin+0.5*\xmax,\ymin)}},
+ y label style/.sseq append to style={/sseqpages/global/every y axis label}{at={(\xmin ,0.5*\ymax+0.5*\ymin)}},
+ label style/.sseq x y alias,
+ draw x label/.sseq is if=drawxaxislabel,
+ draw y label/.sseq is if=drawyaxislabel,
+ no x label/.sseq is if false={drawxaxislabel},
+ no y label/.sseq is if false={drawyaxislabel},
+ no labels/.sseq x y alias=no #1 label,
+ draw labels/.sseq x y alias=draw #1 label,
+%
+ no x axis/.style={x axis type=none},
+ no y axis/.style={y axis type=none},
+ no axes/.style={axes type=none},
+ rotate labels/.sseq is if=rotatelabels,
+%
+ draw x ticks/.sseq is if=drawxaxisticks,
+ no x ticks/.sseq is if false=drawxaxisticks,
+ draw y ticks/.sseq is if=drawyaxisticks,
+ no y ticks/.sseq is if false=drawyaxisticks,
+ no ticks/.sseq x y alias=no #1 ticks,
+ no ticks/.value forbidden,
+ draw ticks/.sseq x y alias=draw #1 ticks,
+ draw ticks/.default=true,
+ tick step/.sseq x y alias,
+ tick step/.value required,
+ x tick step/.sseq store positive int in=\sseq@xtickstep{1},
+ y tick step/.sseq store positive int in=\sseq@ytickstep{1},
+ tick offset/.sseq x y alias,
+ tick offset/.value required,
+ x tick offset/.sseq store int in=\sseq@xtickstepoffset{0},
+ y tick offset/.sseq store int in=\sseq@ytickstepoffset{0},
+ major tick step/.sseq x y alias,
+ x major tick step/.sseq store int in=\sseq@xmajortickstep{0},
+ y major tick step/.sseq store int in=\sseq@ymajortickstep{0},
+ minor tick step/.sseq x y alias,
+ x minor tick step/.sseq store int in=\sseq@xminortickstep{0},
+ y minor tick step/.sseq store int in=\sseq@yminortickstep{0},
+ x tick handler/.code={\def\sseq@xtickfn##1{#1}},
+ x tick handler/.sseq default code={\def\sseq@xtickfn@default##1{#1}},
+ y tick handler/.code={\def\sseq@ytickfn##1{#1}},
+ y tick handler/.sseq default code={\def\sseq@ytickfn@default##1{#1}},
+ class label handler/.code = { % \long so that \ifx\sseq@classname@handler\@gobble compares true if this is empty (\@gobble is long...)
+ \long\def\sseq@classlabel@handler##1{#1}
+ \ifx\sseq@classlabel@handler\@gobble
+ \def\sseq@classlabel@handler##1{\def\result{##1}}
+ \else
+ \edef\sseq@tempa{\unexpanded\@xp{\sseq@classlabel@handler{}}}
+ \edef\sseq@tempb{\unexpanded\@xp{\sseq@classlabel@handler{blah}}}
+ \ifx\sseq@tempa\sseq@tempb
+ \long\def\sseq@classlabel@handler##1{#1{##1}}
+ \fi
+ \fi
+ },
+ class label handler/.sseq default code={
+ \long\def\sseq@classlabel@handler@default##1{#1}
+ \ifx\sseq@classlabel@handler@default\@gobble
+ \def\sseq@classlabel@handler@default##1{\def\result{##1}}
+ \else
+ \edef\sseq@tempa{\unexpanded\@xp{\sseq@classlabel@handler@default{}}}
+ \edef\sseq@tempb{\unexpanded\@xp{\sseq@classlabel@handler@default{blah}}}
+ \ifx\sseq@tempa\sseq@tempb
+ \long\def\sseq@classlabel@handler@default##1{#1{##1}}
+ \fi
+ \fi
+ },
+ class name handler/.code = { % \long so that \ifx\sseq@classname@handler\@gobble compares true if this is empty (\@gobble is long...)
+ \long\def\sseq@classname@handler##1{#1}
+ \ifx\sseq@classname@handler\@gobble
+ \def\sseq@classname@handler##1{\def\result{##1}}
+ \else
+ \edef\sseq@tempa{\unexpanded\@xp{\sseq@classname@handler{}}}
+ \edef\sseq@tempb{\unexpanded\@xp{\sseq@classname@handler{blah}}}
+ \ifx\sseq@tempa\sseq@tempb
+ \long\def\sseq@classname@handler##1{#1{##1}}
+ \fi
+ \fi
+ },
+ class name handler/.sseq default code={
+ \long\def\sseq@classname@handler@default##1{#1}
+ \ifx\sseq@classname@handler@default\@gobble
+ \def\sseq@classname@handler@default##1{\def\result{##1}}
+ \else
+ \edef\sseq@tempa{\unexpanded\@xp{\sseq@classname@handler@default{}}}
+ \edef\sseq@tempb{\unexpanded\@xp{\sseq@classname@handler@default{blah}}}
+ \ifx\sseq@tempa\sseq@tempb
+ \long\def\sseq@classname@handler@default##1{#1{##1}}
+ \fi
+ \fi
+ },
+ x tick style/.sseq store in=\sseq@xtickstyle{},
+ y tick style/.sseq store in=\sseq@ytickstyle{},
+ tick style/.sseq x y alias,
+%
+ axes type/.sseq x y alias=axis type,
+ axes type/.add code={\let\sseq@axistypesanitizer\sseq@axistypesanitizerb}{\let\sseq@axistypesanitizer\sseq@axistypesanitizera},
+ axes type/.value required,
+ x axis type/.sseq store in sanitize=\sseq@xaxistype{border}\sseq@axistypesanitizer,
+ x axis type/.value required,
+ y axis type/.sseq store in sanitize=\sseq@yaxistype{border}\sseq@axistypesanitizer,
+ y axis type/.value required,
+ x axis origin/.sseq store int in=\sseq@xaxisorigin@center@{0},
+ y axis origin/.sseq store int in=\sseq@yaxisorigin@center@{0},
+ axes origin/.sseq x y alias=axis origin,
+}
+
+\def\sseq@validaxestypesa{border, center, or none. The value frame only works with the 'axes type' key.}
+\def\sseq@validaxestypesb{border, center, frame, or none.}
+\let\sseq@validaxestypes\sseq@validaxestypesa
+
+\let\sseq@axistypekeya\@firstofone
+\def\sseq@axistypekeyb#1{axes type}
+\let\sseq@axistypekey\sseq@axistypekeya
+
+\def\sseq@uptoequal#1{\sseq@uptoequal@#1\sseq@nil}
+\def\sseq@uptoequal@#1=#2\sseq@nil{#1}
+\def\sseq@axistypesanitizerb#1#2{
+ \let\sseq@validaxestypes\sseq@validaxestypesb
+ \let\sseq@axistypekey\sseq@axistypekeyb
+ \@xp\ifx\csname sseq@leftgridpadding@y#1\endcsname\relax
+ \sseq@error@xx{invalid-axis-type}{#1}{\sseq@uptoequal{#2}}
+ \sseq@breakfi
+ \else
+ \def\sseq@sanitize@output{#1}
+ \fi
+ \let\sseq@validaxestypes\sseq@validaxestypesa
+ \let\sseq@axistypekey\sseq@axistypekeya
+}
+
+\def\sseq@axistype@frame{frame}
+\def\sseq@axistypesanitizera#1#2{
+ \def\temp{#1}
+ \ifx\sseq@axistype@frame\temp
+ \sseq@error@xx{invalid-axis-type}{#1}{\sseq@uptoequal{#2}}
+ \sseq@breakfi
+ \fi
+ \sseq@axistypesanitizerb{#1}{#2}
+}
+\let\sseq@axistypesanitizer\sseq@axistypesanitizera
+
+\pgfqkeys{/sseqpages}{%
+ class label handler/.code = { % \long so that \ifx\sseq@classlabel@handler\@gobble compares true if this is empty (\@gobble is long...)
+ \long\def\sseq@classlabel@handler##1{#1}
+ \ifx\sseq@classlabel@handler\@gobble
+ \def\sseq@classlabel@handler##1{\def\result{##1}}
+ \else
+ \edef\sseq@tempa{\unexpanded\@xp{\sseq@classlabel@handler{}}}
+ \edef\sseq@tempb{\unexpanded\@xp{\sseq@classlabel@handler{blah}}}
+ \ifx\sseq@tempa\sseq@tempb
+ \long\def\sseq@classlabel@handler##1{#1{##1}}
+ \fi
+ \fi
+ },
+ class name handler/.code = { % \long so that \ifx\sseq@classname@handler\@gobble compares true if this is empty (\@gobble is long...)
+ \long\def\sseq@classname@handler##1{#1}
+ \ifx\sseq@classname@handler\@gobble
+ \def\sseq@classname@handler##1{\def\result{##1}}
+ \else
+ \edef\sseq@tempa{\unexpanded\@xp{\sseq@classname@handler{}}}
+ \edef\sseq@tempb{\unexpanded\@xp{\sseq@classname@handler{blah}}}
+ \ifx\sseq@tempa\sseq@tempb
+ \long\def\sseq@classname@handler##1{#1{##1}}
+ \fi
+ \fi
+ }
+}
+
+\sseq@globalkeys{
+ axes gap/.sseq x y alias=axis gap,
+ axes gap/.value required,
+ x axis gap/.sseq store dimen in=\sseq@xaxisgap{0.5cm},
+ y axis gap/.sseq store dimen in=\sseq@yaxisgap{0.5cm},
+ tick gap/.sseq x y alias,
+ tick gap/.value required,
+ x tick gap/.sseq store dimen in=\sseq@xlabelgap{0.5cm},
+ y tick gap/.sseq store dimen in=\sseq@ylabelgap{0.5cm},
+ x axis tail/.sseq store dimen in=\sseq@xaxis@tail{0.9cm},
+ y axis tail/.sseq store dimen in=\sseq@yaxis@tail{0.9cm},
+ x axis extend start/.sseq store dimen in=\sseq@xaxis@start@extend{0.5cm},
+ y axis extend start/.sseq store dimen in=\sseq@yaxis@start@extend{0.5cm},
+ axes extend start/.sseq x y alias=axis extend start,
+ x axis extend end/.sseq store dimen in=\sseq@xaxis@end@extend{0.5cm},
+ y axis extend end/.sseq store dimen in=\sseq@yaxis@end@extend{0.5cm},
+ axes extend end/.sseq x y alias=axis extend end,
+ x axis clip padding/.sseq store dimen in=\sseq@xclip@axisgap{0.1cm},
+ y axis clip padding/.sseq store dimen in=\sseq@yclip@axisgap{0.1cm},
+ axes clip padding/.sseq x y alias=axis clip padding,
+ right clip padding/.sseq store dimen in=\sseq@clip@padding@right{0.1cm},
+ left clip padding/.sseq store dimen in=\sseq@clip@padding@left{0.1cm},
+ top clip padding/.sseq store dimen in=\sseq@clip@padding@top{0.1cm},
+ bottom clip padding/.sseq store dimen in=\sseq@clip@padding@bottom{0.1cm}
+}
+
+%%%
+%%% Coordinate transformations
+%%%
+
+%%% Global Coordinate Transformations
+\sseq@globaldetonetrue
+\sseq@addtostorelist\sseq@globaldetonetransform{}
+\sseq@addtostorelist\sseq@globalrotatetransform{}
+\sseq@addtostorelist\sseq@globalscaletransform{}
+
+\sseq@addtostorelist\sseq@xscale{1}
+\sseq@addtostorelist\sseq@yscale{1}
+
+\def\sseq@globaldetoneoption#1#2{%
+ \ifsseq@globaldetone\else
+ \sseq@error@n{det-one-after-scale}{#2}
+ \fi
+ \@xp\gdef\@xp\sseq@globaldetonetransform\@xp{\sseq@globaldetonetransform #1}%
+ #1
+}
+\def\sseq@globalscaleoption#1{%
+ \sseq@globaldetonefalse
+ \@ifundefined{sseq@globalscaletransform}{
+ \@xp\gdef\@xp\sseq@globalscaletransform@default\@xp{\sseq@globalscaletransform@default#1}%
+ }{
+ \@xp\gdef\@xp\sseq@globalscaletransform\@xp{\sseq@globalscaletransform#1}%
+ }
+}
+\pgfqkeys{/sseqpages/global}{%
+ x/.sseq @ illegal,
+ y/.sseq @ illegal,
+ z/.sseq @ illegal,
+ xshift/.sseq @ illegal as global,
+ yshift/.sseq @ illegal as global,
+ shift/.sseq @ illegal as global,
+ scale around/.sseq @ illegal as global,
+ rotate around/.sseq @ illegal as global,
+ rotate/.code={%
+ \@xp\gdef\@xp\sseq@globalrotatetransform\@xp{\sseq@globalrotatetransform\pgftransformrotate{#1}}%
+ \sseq@globaldetoneoption{\pgftransformrotate{#1}}{rotate}%
+ },
+ rotate/.value required,
+ xslant/.code={\sseq@globaldetoneoption{\pgftransformxslant{#1}}{xslant}},
+ xslant/.value required,
+ yslant/.code={\sseq@globaldetoneoption{\pgftransformyslant{#1}}{yslant}},
+ yslant/.value required,
+ xmirror/.code={\sseq@globaldetoneoption{\pgftransformxscale{-1}}{xmirror}},
+ xmirror/.value forbidden,
+ ymirror/.code={\sseq@globaldetoneoption{\pgftransformyscale{-1}}{ymirror}},
+ ymirror/.value forbidden,
+ mirror/.forward to=/sseqpages/global/xmirror,
+ mirror/.forward to=/sseqpages/global/ymirror,
+ xscale/.code={%
+ \sseq@ifpgfmathexpr{int(#1-1)}{}{
+ \sseq@error@nn{invalid-scale}{x}{#1}%
+ \sseq@break
+ }
+ \ifpgfmathunitsdeclared
+ \sseq@error@nn{unit-scale}{x}{#1}%
+ \sseq@break
+ \fi
+ \ifnum\sseq@mathresult<\z@
+ \sseq@error@nn{negative-scale}{x}{#1}%
+ \sseq@break
+ \fi
+ \sseq@globalscaleoption{\pgfsetxvec{\pgfpointxy{#1}{0}}}
+ \@ifundefined{sseq@xscale}{
+ \pgfmathparse{\sseq@xscale@default*#1}
+ \edef\sseq@xscale@default{\pgfmathresult}
+ }{
+ \pgfmathparse{\sseq@xscale*#1}
+ \edef\sseq@xscale{\pgfmathresult}
+ }
+ \sseq@breakpoint
+ },
+ xscale/.value required,
+ yscale/.code={%
+ \sseq@ifpgfmathexpr{int(#1-1)}{}{
+ \sseq@error@nn{invalid-scale}{y}{#1}%
+ \sseq@break
+ }
+ \ifpgfmathunitsdeclared
+ \sseq@error@nn{unit-scale}{y}{#1}%
+ \sseq@break
+ \fi
+ \ifnum\sseq@mathresult<\z@
+ \sseq@error@nn{negative-scale}{y}{#1}%
+ \sseq@break
+ \fi
+ \sseq@globalscaleoption{\pgfsetyvec{\pgfpointxy{0}{#1}}}
+ \@ifundefined{sseq@yscale}{
+ \pgfmathparse{\sseq@yscale@default*#1}
+ \edef\sseq@yscale@default{\pgfmathresult}
+ }{
+ \pgfmathparse{\sseq@yscale*#1}
+ \edef\sseq@yscale{\pgfmathresult}
+ }
+ \sseq@breakpoint
+ },
+ yscale/.value required,
+ scale/.sseq x y alias=#1scale,
+ scale/.value required
+}
+
+%%% Local Coordinate transformations
+
+\pgfqkeys{/sseqpages/class placement}{
+ x shift/.code={\sseq@error@nn{class-placement-shift}{x shift}{#1}},
+ y shift/.code={\sseq@error@nn{class-placement-shift}{y shift}{#1}},
+ shift/.code={\sseq@error@nn{class-placement-shift}{shift}{#1}}
+}
+
+%% setup for coordinate transformation keys
+\def\sseq@checktransform{\sseq@error@nn{illegal-transform}}
+\pgfqkeys{/sseqpages}{%
+ x/.code={\sseq@error@n{option-illegal}{#1}},
+ y/.code={\sseq@error@n{option-illegal}{#1}},
+ z/.code={\sseq@error@n{option-illegal}{#1}},
+ xshift/.sseq @ first pass code=\sseq@tikz@firstpass@checkshifttransform{x}{#1},
+ yshift/.sseq @ first pass code=\sseq@tikz@firstpass@checkshifttransform{y}{#1},
+ xshift/.append code=\sseq@options@scopeshiftsecondpass{\sseq@tikz@shifttransform{x}{#1}},%% Note special scopeshiftsecondpass command
+ yshift/.append code=\sseq@options@scopeshiftsecondpass{\sseq@tikz@shifttransform{y}{#1}},%%
+ xshift/.value required,
+ yshift/.value required,
+ shift/.sseq @ transform, shift/.value required,
+ shift only/.sseq @ transform,
+ xscale/.sseq @ transform, xscale/.value required,
+ yscale/.sseq @ transform, yscale/.value required,
+ scale/.sseq @ transform, scale/.value required,
+ scale around/.sseq @ transform, scale around/.value required,
+ xslant/.sseq @ transform, xslant/.value required,
+ yslant/.sseq @ transform, yslant/.value required,
+ rotate/.sseq @ transform, rotate/.value required,
+ rotate around/.sseq @ transform, rotate around/.value required,
+ cm/.sseq @ transform, cm/.value required,
+ reset cm/.sseq @ transform, reset cm/.value forbidden,
+ transform canvas/.sseq @ transform, transform canvas/.value required
+}
+
+\def\sseq@shift@firstpassprocess#1{\sseq@shift@firstpassprocess@#1\sseq@nil}
+\def\sseq@shift@firstpassprocess@#1#2\sseq@nil{\ifx#1\pgfpointxy\sseq@shift@firstpassprocess@@#2\else\sseq@standardparse\fi}
+\def\sseq@shift@firstpassprocess@@#1#2{
+ \sseq@tikz@firstpass@checkshifttransform{x}{#1}
+ \sseq@tikz@firstpass@checkshifttransform{y}{#2}
+}
+
+\def\sseq@shift@secondpassprocess#1{\sseq@shift@secondpassprocess@#1\sseq@nil}
+\def\sseq@shift@secondpassprocess@#1#2\sseq@nil{\ifx#1\pgfpointxy\sseq@shift@secondpassprocess@@#2\else\sseq@standardparse \pgftransformshift{#1#2}\fi}
+\def\sseq@shift@secondpassprocess@@#1#2{
+ \sseq@tikz@shifttransform{x}{#1}
+ \sseq@tikz@shifttransform{y}{#2}
+}
+
+% special second pass handler for scopes to prevent shifts from causing out of memory style errors,
+% see the longer comment at the definition of sseq@scope in main.code.tex
+\def\sseq@options@scopeshiftsecondpass{\sseq@options@secondpass}
+
+\sseq@pgfqkeysdirlist{/sseqpages/scope,/sseqpages/tikz primitives,/sseqpages,/sseqpages/label,/sseqpages/class/label}{
+ align/.sseq @ second pass code=\tikzset{align/#1},
+ align/.sseq @ first pass append code={\sseq@needstikztrue\def\sseq@classquotes@smugglehook@inner{\sseq@needstikztrue\tikzset{align/#1}}}
+}
+
+\sseq@pgfqkeysdirlist{/sseqpages/scope,/sseqpages/tikz primitives}{
+ shift/.code={
+ \sseq@options@firstpass{\tikz@scan@one@point\sseq@shift@firstpassprocess#1\relax}
+ \sseq@options@secondpass{\tikz@scan@one@point\sseq@shift@secondpassprocess#1\relax} % TODO: maybe try to save the work of processing the point twice?
+ },
+ xshift/.sseq @ first pass code=\sseq@tikz@firstpass@checkshifttransform{x}{#1},
+ yshift/.sseq @ first pass code=\sseq@tikz@firstpass@checkshifttransform{y}{#1},
+ xshift/.append code=\sseq@options@scopeshiftsecondpass{\sseq@tikz@shifttransform{x}{#1}},%% Note special scopeshiftsecondpass command
+ yshift/.append code=\sseq@options@scopeshiftsecondpass{\sseq@tikz@shifttransform{y}{#1}},%%
+ rotate around/.sseq @ second pass code={\sseq@standardparse\sseq@pgfkeysdocommand{rotate}{#1}},
+ rotate/.sseq @ stdparse do transform, rotate/.value required,
+ xslant/.sseq @ stdparse do transform, xslant/.value required,
+ yslant/.sseq @ stdparse do transform, yslant/.value required,
+ xmirror/.sseq @ stdparse do transform={-1}, xmirror/.value forbidden,
+ ymirror/.sseq @ stdparse do transform={-1}, ymirror/.value forbidden,
+ xscale/.sseq @ stdparse do transform, xscale/.value required,
+ yscale/.sseq @ stdparse do transform, yscale/.value required,
+ scale/.sseq @ stdparse do transform, scale/.value required,
+ scale around/.sseq @ stdparse do transform, scale around/.value required
+}
+
+%%% Error checking for nonglobal transformations:
+
+\def\sseq@firstpass@checkshifttransform#1#2{%
+ \pgfmathparse{#2}%
+ \sseq@ifpgfmathresultisunitlessint{%
+ \@xp\pgfmathint\@xp{\pgfmathresult}%
+ \@xp\advance\csname sseq@#1\endcsname\pgfmathresult\relax
+ }{\sseq@error@n{illegal-transform}{#1shift}}%
+}
+\let\sseq@firstpass@shifttransform\sseq@firstpass@checkshifttransform
+
+\def\sseq@checkshifttransform#1#2{%
+ \pgfmathparse{(#2)}%
+ \sseq@ifpgfmathresultisunitlessint{%
+ \@xp\pgfmathint\@xp{\pgfmathresult}%
+ \@xp\advance\csname sseq@#1\endcsname\pgfmathresult\relax
+ \sseq@pgfkeysdocommand{#1shift}{#2cm}
+ }{\sseq@error@n{illegal-transform}{#1shift}}%
+}
+
+\def\sseq@okayshifttransform#1#2{%
+ \pgfmathparse{#2}
+ \ifpgfmathunitsdeclared
+ \sseq@pgfkeysdocommand{#1shift}{#2}
+ \else
+ \sseq@pgfkeysdocommand{#1shift}{#2cm}
+ \fi
+}
+
+\def\sseq@tikz@firstpass@checkshifttransform#1#2{%
+ \pgfmathparse{#2}%
+ \sseq@ifpgfmathresultisunitlessint{%
+ \@xp\pgfmathint\@xp{\pgfmathresult}%
+ \@xp\advance\csname sseq@\sseq@getfirstchar#1\sseq@nil\endcsname\pgfmathresult\relax
+ }{\sseq@standardparse}%
+}
+
+\def\sseq@tikz@shifttransform#1#2{%
+ \pgfmathparse{#2}%
+ \sseq@ifpgfmathresultisunitlessint{%
+ \@xp\pgfmathint\@xp{\pgfmathresult}%
+ \@xp\advance\csname sseq@\sseq@getfirstchar#1\sseq@nil\endcsname\pgfmathresult\relax
+ \sseq@pgfkeysdocommand{#1shift}{#2cm*\csname sseq@#1scale\endcsname}
+ }{
+ \sseq@standardparse
+ \ifpgfmathunitsdeclared
+ \sseq@pgfkeysdocommand{#1shift}{#2}
+ \else
+ \sseq@pgfkeysdocommand{#1shift}{#2cm}
+ \fi
+ }%
+}
+
+\def\sseq@ifpgfmathresultisunitlessint{\ifpgfmathunitsdeclared\@xp\@secondoftwo\else\sseq@ifpgfmathresultisint{\@xp\@firstoftwo}{\@xp\@secondoftwo}\fi}
+\def\sseq@ifpgfmathresultisint{\@xp\sseq@ifpgfmathresultisint@\pgfmathresult..\sseq@nil}
+\def\sseq@ifpgfmathresultisint@#1.#2.#3\sseq@nil{\ifx\sseq@nil#2\sseq@nil\@xp\@firstoftwo\else \ifnum#2=\z@ \@xp\@xp\@xp\@firstoftwo\else \@xp\@xp\@xp\@secondoftwo\fi\fi}
+
+%%%% Options for Tikz primitives
+
+\def\sseq@pageconstraint{1}
+\def\sseq@pageconstraint@true{1}
+
+\sseq@pgfqkeysdirlist{/sseqpages/scope,/sseqpages/tikz primitives}{
+ page constraint/.code={
+ \sseq@updatepageconstraint{#1}{&&}
+ },
+ page constraint or/.code={
+ \sseq@updatepageconstraint{#1}{||}
+ },
+ page/.code={\sseq@getnumrange@defaultupperboundinfty\temp{#1}\sseq@updatepageconstraintrange},
+ set page constraint/.store in=\sseq@pageconstraint,
+ background/.code={\sseq@standardparse\sseq@setsavedpaths{background}\pgfkeysalso{standard parse}},
+}
+
+\sseq@pgfqkeysdirlist{/sseqpages/scope,/sseqpages/tikz primitives,/sseqpages/fit}{
+ class style/.code={\sseq@d@addto@options{\sseq@thesseqstyle\sseq@theclassstyle\the\sseq@scope@toks}},
+ cycle style/.forward to=/sseqpages/tikz primitives/class style,
+ permanent class style/.code={\sseq@d@addto@options{\sseq@thesseqstyle\sseq@theclassstyle\sseq@thepermanentcyclestyle\the\sseq@scope@toks}},
+ permanent cycle style/.forward to=/sseqpages/tikz primitives/permanent class style,
+ transient class style/.code={\sseq@d@addto@options{\sseq@thesseqstyle\sseq@theclassstyle\sseq@thetransientcyclestyle\the\sseq@scope@toks}},
+ transient cycle style/.forward to=/sseqpages/tikz primitives/transient class style,
+ this page class style/.code={\sseq@d@addto@options{\sseq@thesseqstyle\sseq@theclassstyle\sseq@thetransientcyclestyle\sseq@thethispagecyclestyle\the\sseq@scope@toks}},
+ this page cycle style/.forward to=/sseqpages/tikz primitives/this page class style,
+ differential style/.code={\sseq@d@addto@options{\sseq@thesseqstyle\sseq@theedgestyle\sseq@thedifferentialstyle\the\sseq@scope@toks}},
+ struct line style/.code={\sseq@d@addto@options{\sseq@thesseqstyle\sseq@theedgestyle\sseq@thestructlinestyle\the\sseq@scope@toks}},
+ tikz primitive style/.code={\sseq@d@addto@options{\sseq@thesseqstyle\sseq@thetikzprimitivestyle\the\sseq@scope@toks}}
+ %this page struct line style/.sseq @ first pass code={\sseq@d@addto@options{\sseq@thesseqstyle\sseq@theedgestyle\sseq@thestructlinestyle\the\sseq@scope@toks}}
+}
diff --git a/graphics/pgf/contrib/spectralsequences/src/sseqloadstore.code.tex b/graphics/pgf/contrib/spectralsequences/src/sseqloadstore.code.tex
new file mode 100644
index 0000000000..19a466112b
--- /dev/null
+++ b/graphics/pgf/contrib/spectralsequences/src/sseqloadstore.code.tex
@@ -0,0 +1,194 @@
+%%
+%% Package: spectralsequences v1.3.0 2021-07-18
+%% Author: Hood Chatham
+%% Email: hood@mit.edu
+%% Date: 2021-07-18
+%% License: Latex Project Public License
+%%
+%% File: sseqloadstore.code.tex
+%%
+%% Defines commands for maintaining separate namespaces between distinct spectral sequences and saving information.
+%%
+
+
+% These are attributes that come with a given spectral sequence (by name)
+% All of these values are loaded at the beginning of sseqpage (and of sseqdata if an existing sseq is being modified).
+% They are then stored together at the end of sseqdata (and of sseqpage if "keep changes" option is present).
+\def\sseq@storelist{}
+\def\sseq@storelist@setdefaults{}
+\def\sseq@addtostorelist#1#2{%
+ \sseq@g@addto@macro\sseq@storelist{\\#1}%
+ \sseq@x@addto@macro\sseq@storelist@setdefaults{\global\let\@nx#1\@xp\@nx\csname \sseq@macroname#1@default\endcsname}%
+ \@xp\gdef\csname\sseq@macroname#1@default\endcsname{#2}%
+}
+% TODO: is this used?
+\def\sseq@addtostorelistlet#1#2{%
+ \sseq@g@addto@macro\sseq@storelist{\\#1}%
+ \sseq@x@addto@macro\sseq@storelist@setdefaults{\global\let\@nx#1\@xp\@nx\csname \sseq@macroname#1@default\endcsname}%
+ \global\@xp\let\csname\sseq@macroname#1@default\endcsname#2%
+}
+
+\def\sseq@addiftostorelist#1{\sseq@addiftostorelistopt{#1}{false}}
+\def\sseq@addiftostorelist@truedefault#1{\sseq@addiftostorelistopt{#1}{true}}
+\def\sseq@addiftostorelistopt#1#2{%
+ \@xp\newif\csname ifsseq@#1\endcsname
+ \sseq@g@addto@macro\sseq@storelist{\sseq@storelistif{#1}}%
+ \sseq@x@addto@macro\sseq@storelist@setdefaults{\@xp\@nx\csname sseq@#1@default\endcsname}%
+ \@xp\xdef\csname sseq@#1@default\endcsname{\global\@xp\@nx\csname sseq@#1#2\endcsname}%
+}
+\def\sseq@addkeytostorelist#1#2{
+ \sseq@g@addto@macro\sseq@storelist{\sseq@storelistkey{#1}}%
+ \sseq@x@addto@macro\sseq@storelist@setdefaults{
+ \global\let\@xp\@nx\csname pgfk@#1/.@cmd\endcsname\@xp\@nx\csname pgfk@#1/.@cmd@default\endcsname
+ \global\let\@xp\@nx\csname pgfk@#1/.@body\endcsname\@xp\@nx\csname pgfk@#1/.@body@default\endcsname
+ }%
+ \pgfkeysdef{#1}{#2}%
+ \global\@xp\let\csname pgfk@#1/.@cmd@default\@xp\endcsname\csname pgfk@#1/.@cmd\endcsname
+ \global\@xp\let\csname pgfk@#1/.@body@default\@xp\endcsname\csname pgfk@#1/.@body\endcsname
+}
+
+% transfer data about the current spectral sequence between \<command> and \<command><sseqname>
+\def\sseq@storelist@store#1{\global\@xp\let\csname\sseq@macroname#1@@@\sseq@thename\endcsname#1}
+\def\sseq@storelist@get#1{\global\@xp\let\@xp#1\csname\sseq@macroname#1@@@\sseq@thename\endcsname}
+
+\def\sseq@storelistif@store#1{%
+ \@xp\edef\csname sseq@storeif@#1\endcsname{\csname ifsseq@#1\endcsname \@xp\@nx\csname sseq@#1true\endcsname\else\@xp\@nx\csname sseq@#1false\endcsname\fi}%
+ \@xp\sseq@storelist@store\csname sseq@storeif@#1\endcsname
+}
+\def\sseq@storelistif@get#1{
+ \@xp\sseq@storelist@get\csname sseq@storeif@#1\endcsname
+ \csname sseq@storeif@#1\endcsname
+}
+
+\def\sseq@storelistkey@store#1{
+ \global\@xp\let\csname pgfk@#1/.@cmd@@@\sseq@thename\@xp\endcsname\csname pgfk@#1/.@cmd\endcsname
+ \global\@xp\let\csname pgfk@#1/.@body@@@\sseq@thename\@xp\endcsname\csname pgfk@#1/.@body\endcsname
+}
+\def\sseq@storelistkey@get#1{
+ \global\@xp\let\csname pgfk@#1/.@cmd\@xp\endcsname\csname pgfk@#1/.@cmd@@@\sseq@thename\endcsname
+ \global\@xp\let\csname pgfk@#1/.@body\@xp\endcsname\csname pgfk@#1/.@body@@@\sseq@thename\endcsname
+}
+
+\def\sseq@storecmds{\let\\\sseq@storelist@store\let\sseq@storelistif\sseq@storelistif@store\let\sseq@storelistkey\sseq@storelistkey@store\sseq@storelist}
+\def\sseq@getcmds{\let\\\sseq@storelist@get\let\sseq@storelistif\sseq@storelistif@get\let\sseq@storelistkey\sseq@storelistkey@get\sseq@storelist}
+
+%%%% New "poor man's OOP" Load/Store
+% Directly stores things into commands that are annotated with the name of the spectral sequence.
+% I use this primarily for commands that won't necessarily exist for every spectral sequence
+% for instance because it has the description of a particular class or differential as part of its name
+\def\sseq@obj@fullname#1{sseq.\sseq@thename.#1}
+\def\sseq@obj#1{\csname sseq.\sseq@thename.#1\endcsname}
+\def\sseq@obj@gdef#1{\@xptwo\gdef\sseq@obj{#1}}
+\def\sseq@obj@xdef#1{\@xptwo\xdef\sseq@obj{#1}}
+\def\sseq@obj@protectedxdef#1{\@xptwo\sseq@protected@xdef\sseq@obj{#1}}
+\def\sseq@obj@xodef#1{\@xp\gdef\csname \sseq@obj@fullname{#1}\@xp\endcsname\@xp}
+\def\sseq@obj@let#1{\@xptwo\let\sseq@obj{#1}} % This shouldn't be used anywhere because all object assignments should be global...
+\def\sseq@obj@glet#1{\global\@xptwo\let\sseq@obj{#1}}
+\def\sseq@obj@gletcsname#1#2{\global\@xp\let\csname\sseq@obj@fullname{#1}\@xp\endcsname\csname#2\endcsname}
+\def\sseq@obj@gletobj#1#2{\global\@xp\let\csname\sseq@obj@fullname{#1}\@xp\endcsname\csname\sseq@obj@fullname{#2}\endcsname}
+\def\sseq@obj@undef#1{\global\@xptwo\let\sseq@obj{#1}\relax}
+\def\sseq@obj@show#1{\@xptwo\show\sseq@obj{#1}}
+\def\sseq@obj@ifundef#1{\@ifundefined{\sseq@obj@fullname{#1}}}
+\def\sseq@obj@ifdef#1#2#3{\@ifundefined{\sseq@obj@fullname{#1}}{#3}{#2}}
+\def\sseq@obj@useifdef@else#1#2{\@ifundefined{\sseq@obj@fullname{#1}}{#2}{\sseq@obj{#1}}}
+\def\sseq@obj@inccheckdef#1{\sseq@obj@ifundef{#1}{\sseq@obj@gdef{#1}{1}}{\sseq@obj@inc{#1}}}
+\def\sseq@obj@inc#1{\sseq@obj@xdef{#1}{\the\numexpr\sseq@obj{#1}+1}}
+\def\sseq@obj@gaddto#1{\sseq@obj@ifundef{#1}{\sseq@obj@gdef{#1}}{\@xptwo\sseq@g@addto@macro\sseq@obj{#1}}}
+\def\sseq@obj@xaddto#1{\sseq@obj@ifundef{#1}{\sseq@obj@xdef{#1}}{\@xptwo\sseq@x@addto@macro\sseq@obj{#1}}}
+\def\sseq@obj@xoaddto#1{\sseq@obj@ifundef{#1}{\sseq@obj@xodef{#1}}{\@xp\sseq@g@addto@macro\csname \sseq@obj@fullname{#1}\@xp\endcsname\@xp}}
+\def\sseq@obj@map#1#2{\def\\##1{#2}\sseq@obj{#1}}
+
+\def\sseq@lettoobj#1#2{\sseq@obj@ifdef{#2}{\@xp\let\@xp#1\csname\sseq@obj@fullname{#2}\endcsname}{\let#1\empty}}
+
+
+\def\sseq@obj@ifchanged#1#2#3{\sseq@obj@ifundef{#1.changed}{#3}{#2}}
+\def\sseq@obj@changedtrue#1{\sseq@obj@gdef{#1.changed}{}}
+\def\sseq@obj@changedfalse#1{\sseq@obj@undef{#1.changed}}
+
+\def\sseq@cleanup@obj#1{
+ \ifsseq@keepchanges\else
+ \sseq@obj@ifchanged{#1}{}{
+ \sseq@obj@changedtrue{#1}
+ \sseq@x@addto@macro\sseq@pagecleanup{
+ \@xptwo\ifx\sseq@obj{#1}\relax
+ \global\@nx\sseq@obj@let{#1}\relax
+ \else
+ \@nx\sseq@obj@gdef{#1}{\unexpanded\@xpthree{\sseq@obj{#1}}}
+ \fi
+ \@nx\sseq@obj@changedfalse{#1}
+ }
+ }
+ \fi
+}
+
+
+%%%% Saved paths, temporary saved paths, background saved paths
+% \sseq@savedpaths@whatever is redefined to be \sseq@savedpaths@whatever@standard or \sseq@savedpaths@whatever@temporary depending on context
+% This simplifies the code a lot because I don't have to check again where I should put things.
+\sseq@addtostorelist\sseq@savedpaths{}
+\def\sseq@tempsavedpaths{}
+\def\sseq@savedpaths@addclass@standard#1{\sseq@x@addto@macro\sseq@savedpaths{\@nx\sseq@class@object{#1}}}
+\def\sseq@savedpaths@adddifferential@standard#1{\sseq@x@addto@macro\sseq@savedpaths{\@nx\sseq@differential@object{#1}}}
+\def\sseq@savedpaths@addextension@standard#1{\sseq@x@addto@macro\sseq@savedpaths{\@nx\sseq@extension@object{#1}}}
+\def\sseq@savedpaths@addstructline@standard#1{\sseq@x@addto@macro\sseq@savedpaths{\@nx\sseq@structline@object{#1}}}
+\def\sseq@savedpaths@xaddtikzpath@standard#1{\sseq@x@addto@macro\sseq@savedpaths{\@nx\sseq@tikzpath@object{#1}}}
+\def\sseq@savedpaths@add@standard{\sseq@g@addto@macro\sseq@savedpaths}
+\def\sseq@savedpaths@xadd@standard{\sseq@x@addto@macro\sseq@savedpaths}
+\def\sseq@savedpaths@show@standard{\show\sseq@savedpaths}
+\def\sseq@scope@savedpaths@add@standard#1{\sseq@savedpaths@add@standard{#1}\sseq@savedpaths@add@background{#1}}
+
+\def\sseq@savedpaths@addclass@temporary#1{\sseq@x@addto@macro\sseq@tempsavedpaths{\@nx\sseq@class@draw@ifpage{#1}}}
+\def\sseq@savedpaths@adddifferential@temporary#1{\sseq@x@addto@macro\sseq@tempsavedpaths{\@nx\sseq@differential@draw@ifpage{#1}}}
+\def\sseq@savedpaths@addextension@temporary#1{\sseq@x@addto@macro\sseq@tempsavedpaths{\@nx\sseq@extension@draw@ifpage{#1}}}
+\def\sseq@savedpaths@addstructline@temporary#1{\sseq@x@addto@macro\sseq@tempsavedpaths{\@nx\sseq@structline@draw@ifpage{#1}}}
+\def\sseq@savedpaths@xaddtikzpath@temporary#1{\sseq@x@addto@macro\sseq@tempsavedpaths{#1}}
+\def\sseq@savedpaths@add@temporary{\sseq@g@addto@macro\sseq@tempsavedpaths}
+\def\sseq@savedpaths@xadd@temporary{\sseq@x@addto@macro\sseq@tempsavedpaths}
+\def\sseq@savedpaths@show@temporary{\show\sseq@tempsavedpaths}
+\def\sseq@scope@savedpaths@add@temporary#1{\sseq@savedpaths@add@temporary{#1}\sseq@savedpaths@add@background{#1}}
+
+
+\def\sseq@savedpaths@addclass@background#1{\sseq@error@internal@n{illegal-add-background-mode}{\sseq@savedpaths@addclass}}
+\def\sseq@savedpaths@adddifferential@background#1{\sseq@error@internal@n{illegal-add-background-mode}{\sseq@savedpaths@adddifferential}}
+\def\sseq@savedpaths@addstructline@background#1{\sseq@error@internal@n{illegal-add-background-mode}{\sseq@savedpaths@addstructline}}
+\def\sseq@savedpaths@addextension@background#1{\sseq@error@internal@n{illegal-add-background-mode}{\sseq@savedpaths@addextension}}
+\def\sseq@savedpaths@xaddtikzpath@background#1{\sseq@x@addto@macro\sseq@savedbackgroundpaths{#1}}
+\def\sseq@savedpaths@add@background{\sseq@g@addto@macro\sseq@savedbackgroundpaths}
+\def\sseq@savedpaths@xadd@background{\sseq@x@addto@macro\sseq@savedbackgroundpaths}
+\def\sseq@savedpaths@show@background{\show\sseq@savedbackgroundpaths}
+
+\def\sseq@savedpaths@addclass@ignore#1{}
+\def\sseq@savedpaths@adddifferential@ignore#1{}
+\def\sseq@savedpaths@addstructline@ignore#1{}
+\def\sseq@savedpaths@addextension@ignore#1{}
+\def\sseq@savedpaths@xaddtikzpath@ignore#1{}
+\def\sseq@savedpaths@add@ignore#1{}
+\def\sseq@savedpaths@xadd@ignore#1{}
+\def\sseq@savedpaths@show@ignore#1{}
+\def\sseq@scope@savedpaths@add@ignore#1{}
+
+\def\sseq@setsavedpaths#1{%
+ \@xp\let\@xp\sseq@savedpaths@addclass \csname sseq@savedpaths@addclass@#1\endcsname
+ \@xp\let\@xp\sseq@savedpaths@adddifferential\csname sseq@savedpaths@adddifferential@#1\endcsname
+ \@xp\let\@xp\sseq@savedpaths@addstructline \csname sseq@savedpaths@addstructline@#1\endcsname
+ \@xp\let\@xp\sseq@savedpaths@addextension \csname sseq@savedpaths@addextension@#1\endcsname
+ \@xp\let\@xp\sseq@savedpaths@xaddtikzpath \csname sseq@savedpaths@xaddtikzpath@#1\endcsname
+ \@xp\let\@xp\sseq@savedpaths@add \csname sseq@savedpaths@add@#1\endcsname
+ \@xp\let\@xp\sseq@savedpaths@xadd \csname sseq@savedpaths@xadd@#1\endcsname
+ \pgfutil@ifundefined{sseq@scope@savedpaths@add@#1}{}{%
+ \@xp\let\@xp\sseq@savedpaths@show \csname sseq@savedpaths@show@#1\endcsname
+ \@xp\let\@xp\sseq@scope@savedpaths@add \csname sseq@scope@savedpaths@add@#1\endcsname
+ }%
+}
+
+% We don't want to add anything to paths except for tikz paths in background mode
+% We need to draw those because they might affect the bounding box.
+\def\sseq@setsavedpaths@draftmode{
+ \sseq@setsavedpaths{ignore} % Don't add stuff to paths
+ % Only add tikzpaths if in background mode (so ignore in standard or temporary)
+ \let\sseq@savedpaths@xaddtikzpath@standard\@gobble
+ \let\sseq@savedpaths@xaddtikzpath@temporary\@gobble
+ % Only update mode for tikzpath
+ \def\sseq@setsavedpaths##1{\@xp\let\@xp\sseq@savedpaths@xaddtikzpath \csname sseq@savedpaths@xaddtikzpath@##1\endcsname}
+}
+
diff --git a/graphics/pgf/contrib/spectralsequences/src/sseqmacromakers.code.tex b/graphics/pgf/contrib/spectralsequences/src/sseqmacromakers.code.tex
new file mode 100644
index 0000000000..b3449eda42
--- /dev/null
+++ b/graphics/pgf/contrib/spectralsequences/src/sseqmacromakers.code.tex
@@ -0,0 +1,529 @@
+%%
+%% Package: spectralsequences v1.3.0 2021-07-18
+%% Author: Hood Chatham
+%% Email: hood@mit.edu
+%% Date: 2021-07-18
+%% License: Latex Project Public License
+%%
+%% File: sseqmacromakers.code.tex
+%% Exposes: \DeclareSseqCommand, \NewSseqCommand, \DeclareSseqGroup, \NewSseqGroup
+%%
+%% Set up commands to define other commands, both the internal macros, and the external ones.
+%% Also the internal macros \sseq@DeclareDocumentCommandAs and \sseq@DeclareDocumentCommand
+%% For the user commands, sets up call stack, thiscall, etc
+%%
+
+\ExplSyntaxOn
+
+%%% Install user commands
+% copy commands into user namespace by removing sseq@ prefixes
+% given a list of commands, \let\thiscommand\sseq@thiscommand on each one
+\def\sseq@installmacros{\@xp\sseq@installmacros@\sseq@macrolist\sseq@nil}
+\def\sseq@installmacros@#1{%
+ \ifx#1\sseq@nil\else % if so, that was the last command in the list
+ \@xp\let\@xp#1\csname sseq@\sseq@macroname#1\endcsname
+ \@xp\sseq@installmacros@
+ \fi %
+}
+
+% Capital U argument type is "Until" but puts back the token when it's done.
+
+% So xparse changed a bunch between the copy pushed to CTAN on February 7th and the copy pushed on February 9th
+\newtoks\sseq@patchxparseUnewcode
+\sseq@patchxparseUnewcode{
+ \cs_new_protected:Npn \sseq__xparse_grab_U:w #1#2 \__xparse_run_code:
+ { \sseq__xparse_grab_U_aux:nnN {#1} {#2} \cs_set_protected_nopar:Npn }
+ \cs_new_protected:Npn \sseq__xparse_grab_U_aux:nnN #1#2#3
+ {
+ \tl_set:Nn \l__xparse_signature_tl {#2}
+ \exp_after:wN #3 \l__xparse_fn_tl ##1 #1
+ { \__xparse_add_arg:n {##1} #1 }
+ \l__xparse_fn_tl
+ }
+ \ifx\__xparse_add_grabber_mandatory:N\undefined
+ \let \__xparse_add_grabber_mandatory:N \__xparse_add_grabber:N
+ \fi
+ \cs_new_protected:Npn \sseq__xparse_add_type_U:w #1
+ {
+ \__xparse_flush_m_args:
+ \__xparse_add_default:
+ \__xparse_add_grabber_mandatory:N U
+ \tl_put_right:Nn \l__xparse_signature_tl { {#1} }
+ \__xparse_prepare_signature:N
+ }
+%
+%
+ \cs_new_protected:Npn \sseq__cmd_grab_U:w #1#2 \__cmd_run_code:
+ { \sseq__cmd_grab_U_aux:nnN {#1} {#2} \cs_set_protected_nopar:Npn }
+ \cs_new_protected:Npn \sseq__cmd_grab_U_aux:nnN #1#2#3
+ {
+ \tl_set:Nn \l__cmd_signature_tl {#2}
+ \exp_after:wN #3 \l__cmd_fn_tl ##1 #1
+ { \__cmd_add_arg:n {##1} #1 }
+ \l__cmd_fn_tl
+ }
+ \cs_new_protected:Npn \sseq__cmd_add_type_U:w #1
+ {
+ \__cmd_flush_m_args:
+ \__cmd_add_default:
+ \__cmd_add_grabber:N U
+ \tl_put_right:Nn \l__cmd_signature_tl { {#1} }
+ \__cmd_prepare_signature:N
+ }
+}
+
+\@ifpackagelater{xparse}{2017/02/08}{
+ \@ifpackagelater{xparse}{2018/10/17}{
+ \ifsseq@patchxparseU
+ \the\sseq@patchxparseUnewcode
+ \else
+ % Assumptions failed, so just make U give an error (this isn't such a big deal since we don't use it for \d anymore).
+ \cs_new_protected:Npn \sseq__cmd_add_type_U:w #1
+ {
+ \sseq@error{U-xparse-incompatible}
+ }
+ \cs_new_eq:NN \sseq__xparse_add_type_U:w \sseq__cmd_add_type_U
+ \fi
+ }{
+ \the\sseq@patchxparseUnewcode
+ }
+}{
+ % OLD CODE
+ \cs_new_protected:Npn \sseq__xparse_grab_U:w #1#2 \l__xparse_args_tl
+ { \sseq__xparse_grab_U_aux:nnN {#1} {#2} \cs_set_protected_nopar:Npn }
+ \cs_new_protected:Npn \sseq__xparse_grab_U_aux:nnN #1#2#3
+ {
+ \exp_after:wN #3 \l__xparse_fn_tl ##1 #1
+ {
+ \__xparse_add_arg:n {##1}
+ #2\l__xparse_args_tl #1
+ }
+ \l__xparse_fn_tl
+ }
+ \cs_new_protected:Npn \sseq__xparse_add_type_U:w #1
+ {
+ \__xparse_flush_m_args:
+ \__xparse_add_grabber_mandatory:N U
+ \tl_put_right:Nn \l__xparse_signature_tl { {#1} }
+ \__xparse_prepare_signature:N
+ }
+}% END \@ifpackagelater
+
+% On 2018/10/01, the commit "Reimplement the ignore_spaces peek functions in terms of peek_spaces" did what it says.
+% Inside of \sseq@DeclareDocumentCommandAs we \let \peek_meaning to \peek_meaning_ignore_spaces
+% which is bad news if \peek_meaning_ignore_spaces is defined in terms of \peek_meaning. This fixes this bug by
+% redefining \peek_meaning_ignore_spaces in terms of a copy of \peek_meaning.
+\@ifpackagelater{expl3}{2018/10/01}{
+ \cs_set_eq:NN\copy_of_peek_meaning:NTF\peek_meaning:NTF
+ \cs_set:Npn\peek_meaning_ignore_spaces:NTF#1#2#3{\peek_remove_spaces:n{\copy_of_peek_meaning:NTF#1{#2}{#3}}}
+ \cs_set_eq:NN\copy_of_peek_meaning_remove:NTF\peek_meaning_remove:NTF
+ \cs_set:Npn\peek_meaning_remove_ignore_spaces:NTF#1#2#3{\peek_remove_spaces:n{\copy_of_peek_meaning_remove:NTF#1{#2}{#3}}}
+}{}
+
+\@ifpackagelater{xparse}{2018/10/17}{
+ \cs_new_protected:Npn \sseq__xparse_normalize_type_U:w #1 {
+ \quark_if_recursion_tail_stop_do:nn {#1} { \__xparse_bad_arg_spec:wn }
+ \__xparse_normalize_check_lu:N U
+ \__xparse_add_arg_spec_mandatory:n { U {#1} }
+ \__xparse_normalize_arg_spec_loop:n
+ }
+
+ \cs_new_protected:Npn \sseq__cmd_normalize_type_U:w #1 {
+ \quark_if_recursion_tail_stop_do:nn {#1} { \__cmd_bad_arg_spec:wn }
+ \__cmd_normalize_check_lu:N U
+ \__cmd_add_arg_spec_mandatory:n { U {#1} }
+ \__cmd_normalize_arg_spec_loop:n
+ }
+}{
+ \cs_new_protected:Npn \sseq__xparse_normalize_type_U:w #1 {
+ \quark_if_recursion_tail_stop_do:nn {#1} { \__xparse_bad_arg_spec:wn }
+ \__xparse_normalize_check_lu:N U
+ \__xparse_add_arg_spec:n { U {#1} }
+ \int_incr:N \l__xparse_mandatory_args_int
+ \tl_clear:N \l__xparse_last_delimiters_tl
+ \__xparse_normalize_arg_spec_loop:n
+ }
+}
+% Expandable commands are a menace to us because they define a bunch of helper commands that we then have to keep track of.
+% We are too lazy to do this, so force \l__cmd_grab_expandably_bool to be false.
+% This is backwards compatible (I think) because \bool_set_false:N just performs a chardef (no existence check)
+% so in old versions, \l__cmd_grab_expandably_bool will be created harmlessly.
+\let\sseq__cmd_prepare_signature:n\__cmd_prepare_signature:n
+\ifx\sseq__cmd_prepare_signature:n\undefined
+ \let\sseq__cmd_prepare_signature:n\__xparse_prepare_signature:n
+\fi
+\pretocmd\sseq__cmd_prepare_signature:n { \bool_set_false:N \l__cmd_grab_expandably_bool } {}{\error}
+
+% I would like to patch the \__xparse_grab_U:w's in my commands into \sseq__xparse_grab_U:w's but I can't because of -NoValue-.
+\def\sseq@install@xparse@Uarggrabber{%
+ \cs_set_eq:NN \__xparse_grab_U:w \sseq__xparse_grab_U:w
+ \cs_set_eq:NN \__cmd_grab_U:w \sseq__cmd_grab_U:w
+}
+
+%%% \sseq@DeclareDocumentCommand
+%
+% \sseq@DeclareDocumentCommand\somecommand is shorthand for \sseq@DeclareDocumentCommandAs\sseq@somecommand\somecommand
+%
+% so the result is that it defines \sseq@somecommand intended to be \let to \somecommand later.
+
+\cs_new_protected:Npn\sseq@DeclareDocumentCommand#1{%
+ \exp_args:Nc \sseq@DeclareDocumentCommandAs@setinputline { sseq @ \cs_to_str:N #1 }#1
+}
+
+\cs_new_protected:Npn\sseq@DeclareDocumentCommandAs@setinputline#1{
+ \protected\edef #1 { \@nx\sseq@setinputline \@xp\@nx\csname \cs_to_str:N #1 @@unique@@ start \endcsname }
+ \exp_args:Nc \sseq@DeclareDocumentCommandAs { \cs_to_str:N #1 @@unique@@ start }
+}
+
+%%% \sseq@DeclareDocumentCommandAs
+%
+% #1 - the command to define
+% #2 - the command to use in argument parsing errors
+% #3 - parameters (\NewDocumentCommand style)
+% #4 - code
+%
+% See my stack exchange question: https://tex.stackexchange.com/questions/350596/control-error-messages-made-by-commands-defined-with-newdocumentcommand
+
+\cs_new_protected:Npn\sseq@DeclareDocumentCommandAs#1#2#3#4{
+ \group_begin:
+ \cs_set_eq:NN \__cmd_add_type_U:w \sseq__cmd_add_type_U:w
+ \cs_set_eq:NN \__xparse_add_type_U:w \sseq__xparse_add_type_U:w
+ \cs_set_eq:NN \__cmd_normalize_type_U:w \sseq__cmd_normalize_type_U:w
+ \cs_set_eq:NN \__xparse_normalize_type_U:w \sseq__xparse_normalize_type_U:w
+ \cs_set_eq:NN \__cmd_prepare_signature:n \sseq__cmd_prepare_signature:n
+ \cs_set_eq:NN \__xparse_prepare_signature:n \sseq__cmd_prepare_signature:n
+ %\cs_set:Npn \__cmd_declare_cmd_code:Nnn {\bool_set_false:N \l__cmd_grab_expandably_bool\__cmd_declare_cmd_code_aux:Nnn}
+ \sseq@installmsghooks
+ \def\sseq@error@setup{}
+ \def\sseq@error@cleanup{\sseq@errortrue}
+ \let\sseq@error@annotation\empty
+ \DeclareDocumentCommand #2 { #3 } { #4 }
+ \ifsseq@error
+ \group_end:
+ \sseq@errortrue % This only happens if it came from user code, so don't set error to be false so it can break out too.
+ \@xp\sseq@break
+ \fi
+ \cs_if_exist:cTF{ \cs_to_str:N #2 \c_space_tl code }{
+ \cs_gset:Npx #1{
+ \begingroup
+ \exp_not:N \cs_set_eq:NN % force xparse to always ignore spaces!
+ \exp_not:N \peek_meaning:NTF
+ \exp_not:N \peek_meaning_ignore_spaces:NTF
+ \exp_not:N \cs_set_eq:NN % force xparse to always ignore spaces!
+ \exp_not:N \peek_meaning_remove:NTF
+ \exp_not:N \peek_meaning_remove_ignore_spaces:NTF
+ % Not sure why we use set_eq:cc and exp_not:n here but I'm concerned that something will break if I change it...
+ \exp_not:N \cs_set_eq:cc
+ \exp_not:n {{ \cs_to_str:N #2 \c_space_tl code }}
+ \exp_not:n {{ \cs_to_str:N #1 \c_space_tl code }}
+ \exp_not:N \cs_set_eq:NN
+ \exp_not:c { \cs_to_str:N #2 \c_space_tl }
+ \exp_not:c { \cs_to_str:N #1 \c_space_tl }
+ \exp_not:c { \cs_to_str:N #1 \c_space_tl inner }
+ }
+ \pretocmd:cnnn { \cs_to_str:N #2 \c_space_tl code } { \endgroup{} } { } { \sseq@error@x { macro-patch-failed } { \string#1 } }
+ \cs_gset_eq:cN { \cs_to_str:N #1 \c_space_tl inner } #2
+ \cs_gset_eq:cc { \cs_to_str:N #1 \c_space_tl code } { \cs_to_str:N #2 \c_space_tl code }
+ \cs_gset_eq:cc { \cs_to_str:N #1 \c_space_tl }{ \cs_to_str:N #2 \c_space_tl } % sometimes the new version of xparse stores stuff in #2<space>
+ }{
+ \cs_gset_eq:NN #1 #2
+ }
+ \group_end: % In this case, I want to pass \sseq@errortrue out of the block, so I close the group before I break.
+ \sseq@breakpoint % So this breakpoint definitely should be after the \endgroup.
+}
+
+\cs_set_eq:NN \patchcmd:Nnnnn \patchcmd
+\cs_set_eq:NN \pretocmd:Nnnn \pretocmd
+\cs_generate_variant:Nn \patchcmd:Nnnnn {cnfnn}
+\cs_generate_variant:Nn \pretocmd:Nnnn {cfnn}
+\cs_generate_variant:Nn \pretocmd:Nnnn {cnnn}
+
+\cs_new_protected:Npn\NewSseqCommand#1{%
+ \cs_if_free:cTF { sseq @ usermacro @ \cs_to_str:N #1 } { \DeclareSseqCommand#1 } { \sseq@error@x{usermacro-not-free}{\string#1} \use_none:nn }
+}
+\let\begingroupa\begingroup
+\let\begingroupb\begingroup
+\newtoks\sseq@macro@setthiscall@toks
+\newtoks\sseq@macro@defaultarggetters@toks
+
+\cs_new_protected:Npn\DeclareSseqCommand#1#2#3{%
+ \cs_if_exist:cTF { sseq @ \cs_to_str:N #1 @@unique@@ start} {\sseq@error@x{wont-override-builtin}{\string#1}\sseq@break} {}
+ \cs_if_free:cT { sseq @ usermacro @ \cs_to_str:N #1 } {
+ \sseq@x@addto@macro { \sseq@installmacros } { \let \exp_not:N #1 \exp_not:c { sseq @ usermacro @ \cs_to_str:N #1 } }
+ }
+ % Make sure \cs_if_exist doesn't pass because there was a previously defined user macro with same name
+ \cs_undefine:c { sseq @ usermacro @ \cs_to_str:N #1 \c_space_tl code }
+ \exp_args:Nc \sseq@DeclareDocumentCommandAs@setinputline { sseq @ usermacro @ \cs_to_str:N #1 } #1 { #2 } {%
+ \sseq@loadinputline
+ \sseq@atbeginusermacro@msgsetup
+ {}#3{} % prevent space patching space hazards with these {}'s
+ \endgroup
+ }%
+ \ifsseq@error
+ \global\sseq@errorfalse
+ \@xp\sseq@break
+ \fi
+ \sseq@parseargspec{#1}{#2}%
+ % You might think we could skip this patching, and it's probably possible.
+ % However, this is responsible for turning the #'s of catcode other into #'s of catcode arg. I don't have as good of a way to do that.
+ % Also, this allows me to use \sseq@parseargspec after defining the command, so I don't have to run any error checking inside of it
+ % because if the argspec is invalid, \DeclareDocumentCommand will let me know.
+ \cs_if_exist:cTF{ sseq @ usermacro @ \cs_to_str:N #1 @@unique@@ start \c_space_tl code }{
+ \patchcmd:cnfnn{ sseq @ usermacro @ \cs_to_str:N #1 @@unique@@ start \c_space_tl code }{\endgroup{}}{
+ \@xp\endgroup
+ \@xp\begingroupa
+ \the\sseq@macro@setthiscall@toks
+ }{}{\sseq@error@x{usermacro-failed-patch}{\string#1}}
+ \cs_gset_eq:cc { sseq @ usermacro @ \cs_to_str:N #1 @@unique@@ start \c_space_tl code } { sseq @ usermacro @ \cs_to_str:N #1 @@unique@@ start \c_space_tl code}
+ }{
+ \pretocmd:cfnn{ sseq @ usermacro @ \cs_to_str:N #1 @@unique@@ start }{
+ \@xp\begingroupb
+ \the\sseq@macro@setthiscall@toks
+ }{}{\sseq@error@x{usermacro-failed-patch}{\string#1}}
+ }
+ \cs_gset_eq:cc { sseq @ usermacro @ \cs_to_str:N #1 } { sseq @ usermacro @ \cs_to_str:N #1 }
+ \cs_gset_eq:cc { sseq @ usermacro @ \cs_to_str:N #1 @@unique@@ start } { sseq @ usermacro @ \cs_to_str:N #1 @@unique@@ start}
+ \ifsseq@inprogress
+ \cs_set_eq:Nc #1 { sseq @ usermacro @ \cs_to_str:N #1 }
+ \fi
+ \sseq@breakpoint
+}
+
+%%% Commands to help the user define "groups" of commands to be reused
+% #1 -- command
+% #2 -- argspec
+% #3 -- body of function (long)
+\NewDocumentCommand\NewSseqGroup{mm+m}{\DeclareSseqGroup@\NewSseqCommand{#1}{#2}{#3}}
+\NewDocumentCommand\DeclareSseqGroup{mm+m}{\DeclareSseqGroup@\DeclareSseqCommand{#1}{#2}{#3}}
+
+% #1 -- \NewSseqCommand or \DeclareSseqCommand
+% #2 -- command
+% #3 -- argspec
+% #4 -- body of function (long)
+\long\def\DeclareSseqGroup@#1#2#3#4{%
+ \group_begin:
+ \let\sseq@parseargspec\@gobbletwo % Get rid of argparse so we can change what gets added for setting up thiscall
+% why are we doing these things here?
+ \sseq@installmsghooks
+ \def\sseq@error@setup{}
+ \def\sseq@error@cleanup{\sseq@errortrue}
+ \let\sseq@error@annotation\empty
+%
+% undefine so we can tell whether we need to patch \command<space>code or \command
+ \cs_undefine:c { sseq @ usermacro @ \cs_to_str:N #2 @ helper \c_space_tl code }
+ \exp_args:Nc \sseq@DeclareDocumentCommandAs { sseq @ usermacro @ \cs_to_str:N #2 @ helper } #2 { #3 } {
+ \sseq@loadinputline
+ \sseq@usermacro@esetthiscall{\the\sseq@groupthiscalltoks}
+ \sseq@atbeginusermacro@msgsetup % the stack push happens in here
+ \sseq@scopecall
+ {}#4{}
+ \end{scope}
+ \endgroup
+ \sseq@breakpoint
+ }%
+ \ifsseq@error
+ \@xp\sseq@break
+ \fi
+ \sseq@macro@setthiscall@toks\@xp{\sseq@SseqGroup@argspec} % This gets added to the command by DeclareSseqCommand
+ #1#2{od()}{%
+ %can't use \sseq@atbeginusermacro@msgsetup until next spot b/c don't know what the whole call looks like yet
+ \IfNoValueTF{##1}{\def\sseq@options{}}{\def\sseq@options{##1}}%
+ \IfNoValueTF{##2}{%
+ \edef\sseq@scopecall{\@nx\begin{scope}[\unexpanded\@xp{\sseq@options}]}
+ }{%
+ \sseqnewgroup@splitcoord##2\sseq@nil
+ }%
+ \csname sseq @ usermacro @ \sseq@macroname #2 @ helper \@xp\endcsname\@gobbletwo % This gobble eats the endgroup added by sseqDeclareDocumentCommand
+ }
+ \sseq@parseargspec@newgroup{#2}{#3}%
+ % You might think we could skip this patching, and it's probably possible.
+ % However, this is responsible for turning the #'s of catcode other into #'s of catcode arg. I don't have as good of a way to do that.
+ \cs_if_exist:cTF{ sseq @ usermacro @ \cs_to_str:N #2 @ helper \c_space_tl code }{
+ \pretocmd:cfnn { sseq @ usermacro @ \cs_to_str:N #2 @ helper \c_space_tl code }{
+ \the\sseq@macro@setthiscall@toks
+ }{}{\sseq@error@x{usermacro-failed-patch}{\string#1}}
+ % globalize definition:
+ \cs_gset_eq:cc { sseq @ usermacro @ \cs_to_str:N #2 @ helper \c_space_tl code } { sseq @ usermacro @ \cs_to_str:N #2 @ helper \c_space_tl code }
+ }{
+ \pretocmd:cfnn { sseq @ usermacro @ \cs_to_str:N #2 @ helper }{
+ \the\sseq@macro@setthiscall@toks
+ }{}{\sseq@error@x{usermacro-failed-patch}{\string#1}}
+ }
+ \cs_gset_eq:cc { sseq @ usermacro @ \cs_to_str:N #2 @ helper } { sseq @ usermacro @ \cs_to_str:N #2 @ helper } % globalize definition
+ \let\sseq@parseargspec\sseq@parseargspec@normal
+ \sseq@breakpoint
+ \group_end:
+}
+
+\def\sseqnewgroup@splitcoord#1,#2\sseq@nil{
+ \sseq@ifintexpr{#1}{}{
+ \def\sseq@scopecall{\sseq@error@n{invalid-coordinate}{x~}\sseq@break}
+ \sseq@break
+ }
+ \sseq@ifintexpr{#2}{}{
+ \def\sseq@scopecall{\sseq@error@n{invalid-coordinate}{y~}\sseq@break}
+ \sseq@break
+ }
+ \edef\sseq@scopecall{
+ \@nx\begin{scope}[xshift=\the\numexpr#1\relax,yshift=\the\numexpr#2\relax,\unexpanded\@xp{\sseq@options}]
+ }
+ \sseq@breakpoint
+}
+
+
+\newtoks\sseq@groupargspectoks
+\newtoks\sseq@groupthiscalltoks
+\bgroup\catcode`\#=12\relax
+ \gdef\sseq@SseqGroup@argspec{
+ \sseq@eval{\global\sseq@groupargspectoks{\IfNoValueF{#1}{\unexpanded{[#1]}}\IfNoValueF{#2}{\unexpanded{(#2)}}}}
+ \@gobbletwo % What does this \@gobbletwo do?
+ }
+ \gdef\sseq@thearg{#\the\sseq@tempcount}
+\egroup
+
+
+% When there are arguments with default values (O, D, R, G), we need to put them
+% into temporary macros to compare them and see if they are the default value
+% that's what \sseq@macro@defaultarggetters@toks is for.
+\def\sseq@parseargspec#1#2{%
+ \sseq@tempcount=\z@
+ \sseq@temptoks{\@nx#1}% Holds the stuff that goes in \esetthiscall (so most stuff)
+ \sseq@macro@defaultarggetters@toks{}
+ \sseq@parseargspec@#2\sseq@nil
+ \sseq@eval{\sseq@macro@setthiscall@toks{\the\sseq@macro@defaultarggetters@toks\@nx\sseq@usermacro@esetthiscall{\the\sseq@temptoks}}}
+}
+
+% For NewGroup:
+\let\sseq@parseargspec@normal\sseq@parseargspec
+\def\sseq@parseargspec@newgroup#1#2{%
+ \sseq@tempcount=\z@
+ \sseq@temptoks{}
+ \sseq@macro@defaultarggetters@toks{}%
+ \sseq@parseargspec@#2\sseq@nil
+ \sseq@eval{
+ \sseq@macro@setthiscall@toks{
+ \the\sseq@macro@defaultarggetters@toks
+ \@nx\sseq@esetthiscall{
+ \@nx\@nx\@nx#1
+ \@nx\the\sseq@groupargspectoks
+ \the\sseq@temptoks
+ }
+ \global\sseq@groupthiscalltoks\@nx\@xp{\@nx\the\sseq@thiscalltoks}
+ }
+ }
+}
+
+
+\def\sseq@parseargspec@#1{
+ \advance\sseq@tempcount\@ne
+ \ifx#1\sseq@nil\else
+ \@xp\ifx\csname sseq@parseargspec@#1\@xp\endcsname\relax
+ \sseq@error@n{usermacro-unsupported-argument-type}{#1}
+ \else
+ \csname sseq@parseargspec@#1\@xptwo\endcsname
+ \fi
+ \fi
+}
+
+
+% Have to use \@alph to convert the number to a letter. Using a number here doesn't work because the control sequence gets retokenized
+% so we can only use letters in control sequences (another option would be to not expand the \csnames till later, but that would be harder).
+\def\sseq@parseargspec@ifargisnotdefault{%
+ \@nx\ifx
+ \@xp\@nx\csname sseq@parseargspec@temparg\@alph\sseq@tempcount\endcsname
+ \@xp\@nx\csname sseq@parseargspec@tempdefault\@alph\sseq@tempcount\endcsname
+ \unexpanded{%
+ \@xp\@gobble
+ \else
+ \@xp\@firstofone
+ \fi
+ }%
+}
+
+\def\sseq@parseargspec@setargdefault#1{%
+ \sseq@e@addto@toks\sseq@macro@defaultarggetters@toks{%
+ \def\@xp\@nx\csname sseq@parseargspec@temparg\@alph\sseq@tempcount\endcsname{\sseq@thearg}%
+ \def\@xp\@nx\csname sseq@parseargspec@tempdefault\@alph\sseq@tempcount\endcsname{\unexpanded{#1}}%
+ }
+}
+
+% v, E, and e are illegal
+
+% Mandatory
+\def\sseq@parseargspec@m{%
+ \sseq@e@addto@temptoks{{\@nx\unexpanded{\sseq@thearg}}}
+ \sseq@parseargspec@
+}
+\def\sseq@parseargspec@l{%
+ \sseq@e@addto@temptoks{\@nx\unexpanded{\sseq@thearg}}
+ \sseq@parseargspec@
+}
+
+\def\sseq@parseargspec@u#1{%
+ \sseq@e@addto@temptoks{\@nx\unexpanded{\sseq@thearg}\unexpanded{\unexpanded{#1}}}
+ \sseq@parseargspec@
+}
+
+\def\sseq@parseargspec@U#1{%
+ \sseq@e@addto@temptoks{\@nx\unexpanded{\sseq@thearg}}
+ \sseq@parseargspec@
+}
+
+% Optional no default
+\def\sseq@parseargspec@o{%
+ \sseq@e@addto@temptoks{\@nx\IfNoValueF{\sseq@thearg}{[\@nx\unexpanded{\sseq@thearg}]}}
+ \sseq@parseargspec@
+}
+
+\def\sseq@parseargspec@d#1#2{%
+ \sseq@e@addto@temptoks{\@nx\IfNoValueF{\sseq@thearg}{\@nx\unexpanded{#1\sseq@thearg#2}}}
+ \sseq@parseargspec@
+}
+
+\def\sseq@parseargspec@r#1#2{ % technically required, but behaves like optional
+ \sseq@e@addto@temptoks{#1\@nx\unexpanded{\sseq@thearg}#2}
+ \sseq@parseargspec@
+}
+
+
+\def\sseq@parseargspec@g{%
+ \sseq@e@addto@temptoks{\@nx\IfNoValueF{\sseq@thearg}{\@nx\unexpanded{{\sseq@thearg}}}}
+ \sseq@parseargspec@
+}
+
+\def\sseq@parseargspec@s{%
+ \sseq@e@addto@temptoks{\@nx\IfBooleanT{\sseq@thearg}{*}}
+ \sseq@parseargspec@
+}
+
+\def\sseq@parseargspec@t#1{%
+ \sseq@e@addto@temptoks{\@nx\IfBooleanT{\sseq@thearg}{#1}}
+ \sseq@parseargspec@
+}
+
+% Optional with default
+
+\def\sseq@parseargspec@O#1{
+ \sseq@parseargspec@setargdefault{#1}%
+ \sseq@e@addto@temptoks{\sseq@parseargspec@ifargisnotdefault{\@nx\unexpanded{[\sseq@thearg]}}}%
+ \sseq@parseargspec@
+}
+\def\sseq@parseargspec@D#1#2#3{%
+ \sseq@parseargspec@setargdefault{#3}%
+ \sseq@e@addto@temptoks{\sseq@parseargspec@ifargisnotdefault{\@nx\unexpanded{#1\sseq@thearg#2}}}%
+ \sseq@parseargspec@
+}
+\def\sseq@parseargspec@R#1#2#3{%
+ \sseq@parseargspec@setargdefault{#3}%
+ \sseq@e@addto@temptoks{\sseq@parseargspec@ifargisnotdefault{\@nx\unexpanded{#1\sseq@thearg#2}}}%
+ \sseq@parseargspec@
+}
+\def\sseq@parseargspec@G#1{%
+ \sseq@parseargspec@setargdefault{#1}%
+ \sseq@e@addto@temptoks{\sseq@parseargspec@ifargisnotdefault{\@nx\unexpanded{{\sseq@thearg}}}}%
+ \sseq@parseargspec@
+}
+
+\ExplSyntaxOff
+
+% \ No newline at end of file
diff --git a/graphics/pgf/contrib/spectralsequences/src/sseqmain.code.tex b/graphics/pgf/contrib/spectralsequences/src/sseqmain.code.tex
new file mode 100644
index 0000000000..6456f99b4b
--- /dev/null
+++ b/graphics/pgf/contrib/spectralsequences/src/sseqmain.code.tex
@@ -0,0 +1,1966 @@
+%%
+%% Package: spectralsequences v1.3.0 2021-07-18
+%% Author: Hood Chatham
+%% Email: hood@mit.edu
+%% Date: 2021-07-18
+%% License: Latex Project Public License
+%%
+%% File: sseqmain.code.tex
+%%
+%% Defines the main environments and commands
+%%
+
+
+%%%%%%%
+%%%
+%%% Environment definitions
+%%%
+%%%%%%%
+\def\sseq@usesavedpaths{%
+ \sseq@savedpaths
+ \sseq@clearchangestyles
+ \sseq@tempsavedpaths
+ \gdef\sseq@tempsavedpaths{}%
+}
+
+\sseq@addtostorelist\sseq@savedbackgroundpaths{}
+\sseq@addtostorelist\sseq@restorestyles{}
+
+\def\sseq@page{\protect\sseq@thepage}
+
+
+\def\sseqdata{\@ifnextchar[{\sseqdata@}{\sseqdata@[]}}%}
+
+\def\sseqdata@[#1]{%
+ \edef\sseq@restorefont{\font@name}%
+ \selectfont\nullfont % Avoid space hazards!
+
+ \def\par{}%
+%
+ \ifx\pgfpictureid\@undefined\else %Uh-oh, we're inside tikz.
+ \sseq@error@n{env-inside-tikz}{sseqdata}\@xp\sseq@breakdataenv
+ \fi
+ \ifsseq@inprogress % Uh-oh, we're already in a sseq environment.
+ \sseq@error@n{env-inside-sseq}{sseqdata}\@xp\sseq@breakdataenv
+ \fi
+ \sseq@inprogresstrue
+ \def\sseq@sseqsetdirectory{/sseqpages}
+ \sseq@keepchangestrue
+ \sseq@storelist@setdefaults
+ \let\sseq@xtickfn\@firstofone\let\sseq@ytickfn\@firstofone % These are default functions, which addtostorelist can't handle.
+%
+ \ifx\sseq@xmin@default\sseq@infinity
+ \let\sseq@xminmax\sseq@xminmax@active
+ \fi
+ \ifx\sseq@ymin@default\sseq@infinity
+ \let\sseq@yminmax\sseq@yminmax@active
+ \fi
+ \ifsseq@mathnodes
+ \let\sseq@maybemathswitch=$\@gobble$
+ \fi
+%
+ % copy commands into the user namespace.
+ \sseq@installmacros
+ \sseq@installmsghooks
+ \sseq@install@xparse@Uarggrabber
+ % The behavior of xmax and xmin is very particular, so it's best not to mess with them.
+ % In particular, they shouldn't be expanded until printing no matter what, but we don't want
+ % \pgfmathparse to barf on them. \pgfmathparse really doesn't like engine protected macros,
+ % so in no case should this be engine protected. (Wish we could test an argument for the presence
+ % of engine protected macros). If we try to do that, \sseq@ifpgfmathexpr will break in
+ % \sseq@tikzprimitives@coords@notaclass@handle@.
+ \def\xmin{\protect\sseq@xmin}\def\xmax{\protect\sseq@xmax}
+ \def\ymin{\protect\sseq@ymin}\def\ymax{\protect\sseq@ymax}
+%
+ \sseq@modtikzcommands % Defer evaluation of tikz primitives
+ \sseq@patchfor % patch \foreach to work with deferred macros
+ \pgfkeys{/handlers/first char syntax/the character "/.initial=\sseq@handlequote} % install quotes parsing
+%
+%
+ \pgfqkeys{/sseqpages/global/name only}{#1}
+ \@ifundefined{sseq@thename}{\sseq@error{env-data-no-name}\sseq@breakdataenv}{}
+ \sseq@obj@ifundef{exists}{
+ \sseq@obj@gdef{exists}{}
+ \pgfqkeys{/sseqpages/global}{{\sseq@thename} options/.code={}}
+ }{
+ \ifsseq@updateexisting\else
+ \sseq@error@x{env-data-already-exist}{\sseq@thename}% this could be a warning
+ \fi
+ \sseq@restorestyles
+ \sseq@getcmds
+
+ }
+ \sseq@setsavedpaths{standard}
+%
+ % the default option handler for /sseqpages/global adds to \sseq@currentoptionpath options
+ \let\sseq@currentoptionpath\sseq@thename
+ \pgfqkeys{/sseqpages/global}{#1}
+ \ifx\sseq@targetx\pgfutil@empty\else
+ \sseq@hasdegreetrue
+ \fi
+ \sseq@eval{
+ \global\let\@xp\@nx\csname pgfk@/sseqpages/global/{\sseq@currentoptionpath} options/.@cmd\endcsname
+ \@xp\@nx\csname pgfk@/sseqpages/global/{\sseq@currentoptionpath} options/.@cmd\endcsname
+ }
+ \sseq@savedpaths@xadd{\@nx\sseq@style@object{\sseq@stylelist}}
+ \sseq@savedpaths@add{\begingroup\sseq@startcontentmarker} % prevent some of the two-pass user macros horrors
+ \let\sseq@error@setup\sseq@error@setup@withinfo
+}
+\let\sseq@startcontentmarker\relax
+
+% Just store all the settings
+\def\endsseqdata{%
+ \let\sseq@error@setup\sseq@error@setup@noinfo
+ \sseq@checkend{sseqdata}%
+ \sseq@savedpaths@add{\endgroup} % prevent some of the two-pass user macros horrors
+ \xdef\sseq@restorestyles{\sseq@stylelist}%
+ \sseq@storecmds
+ \ignorespacesafterend
+ \sseq@breakpoint
+}
+
+\def\sseqpage{\@ifnextchar[{\sseqpage@}{\sseqpage@[]}} %]
+
+\def\sseqpage@[#1]{%
+ \edef\sseq@restorefont{\font@name}%
+ \selectfont\nullfont % Avoid space hazards!
+ \def\par{}%
+%
+ \ifx\pgfpictureid\@undefined\else %Uh-oh, we're inside tikz.
+ \sseq@error@n{env-inside-tikz}{sseqpage}\@xp\sseq@breakpageenv
+ \fi
+ \ifsseq@inprogress % Uh-oh, we're already in a sseq environment. This is not good.
+ \sseq@error@n{env-inside-sseq}{sseqpage}\@xp\sseq@breakpageenv
+ \fi
+ \sseq@inprogresstrue
+ \sseq@ispageenvtrue
+ \def\sseq@sseqsetdirectory{/sseqpages}
+%
+% \ifsseq@mathnodes
+% \let\sseq@maybemathswitch=$\@gobble$
+% \fi
+%
+ % Copy commands into user namespace
+ \sseq@installmacros
+ \sseq@installmsghooks
+ \sseq@install@xparse@Uarggrabber
+ \def\xmin{\protect\sseq@xmin}\def\xmax{\protect\sseq@xmax}
+ \def\ymin{\protect\sseq@ymin}\def\ymax{\protect\sseq@ymax}
+ \sseq@modtikzcommands % Redefine tikz primitives to store themselves in the appropriate place
+ \sseq@patchfor % Change foreach to record it's variable values in the appropriate places
+ \pgfkeys{/handlers/first char syntax/the character "/.initial=\sseq@handlequote}% install quotes parsing
+%
+ \gdef\sseq@pagecleanup{}% For undoing local option settings
+%
+ \def\sseq@thepage{0}% page defaults to 0
+ \def\thepage{\sseq@printpageas}
+ \def\page{\sseq@printnum@inftytosymbol{\sseq@thepage}}
+ \sseq@thepagecount=\z@
+ \pgfqkeys{/sseqpages/global/name only}{#1}% Get the name
+ \sseq@tempiftrue
+ \ifsseq@hasname
+ \sseq@obj@ifundef{exists}{
+ \sseq@error@x{env-page-not-exist}{\sseq@thename} % This could be a warning
+ \sseq@obj@gdef{exists}{}
+ \let\sseq@currentoptionpath\sseq@thename
+ \pgfqkeys{/sseqpages/global}{{\sseq@thename} options/.code={}}
+ \sseq@keepchangestrue
+ \sseq@keepglobaloptionstrue
+ \sseq@setsavedpaths{standard}
+ }{
+ \sseq@restorestyles
+ \sseq@getcmds
+ \ifsseq@keepchanges
+ \sseq@setsavedpaths{standard}
+ \else
+ \sseq@setsavedpaths{temporary}
+ \fi
+ \sseq@tempiffalse
+ \def\sseq@currentoptionpath{this page}
+ \pgfqkeys{/sseqpages/global}{{this page} options/.code={},#1}
+ }
+ \else
+ \edef\sseq@thename{temp@sseq@number@\the\sseq@anonsseqcount}
+ \global\advance\sseq@anonsseqcount1
+ \sseq@setsavedpaths{temporary}
+ \fi
+ \ifsseq@tempif % Either the spectral sequence has no name, or it has a name but no such existed.
+ \sseq@storelist@setdefaults
+ \sseq@savedpaths@xadd{\sseq@stylelist}
+ \let\sseq@currentoptionpath\sseq@thename
+ \pgfqkeys{/sseqpages/global}{{\sseq@thename} options/.code={},{this page} options/.code={},#1}
+ \sseq@eval{
+ \global\let\@xp\@nx\csname pgfk@/sseqpages/global/{\sseq@currentoptionpath} options/.@cmd\endcsname
+ \@xp\@nx\csname pgfk@/sseqpages/global/{\sseq@currentoptionpath} options/.@cmd\endcsname
+ }
+ \fi
+%
+ \ifsseq@specifiedxrange
+ \let\sseq@xminmax\@gobbletwo
+ \else
+ \let\sseq@xminmax\sseq@xminmax@active
+ \fi
+ \ifsseq@specifiedyrange
+ \let\sseq@yminmax\@gobbletwo
+ \else
+ \let\sseq@yminmax\sseq@yminmax@active
+ \fi
+%
+ \sseq@savedpaths@xadd{\sseq@stylelist}
+ \sseq@savedpaths@add{\begingroup} % prevent some of the two-pass user macros horrors
+ \ifx\sseq@targetx\pgfutil@empty\else
+ \sseq@hasdegreetrue
+ \fi
+ \let\sseq@error@setup\sseq@error@setup@withinfo
+}
+
+% Here we do all of the work to actually print the page
+\def\endsseqpage{
+ \let\sseq@error@setup\sseq@error@setup@noinfo
+ \sseq@checkend{sseqpage}
+ \sseq@savedpaths@add{\endgroup} % prevent some of the two-pass user macros horrors
+%
+ \sseq@xscalecm=\sseq@xscale cm
+ \sseq@yscalecm=\sseq@yscale cm
+%
+% check that range is okay
+%
+ \ifx\sseq@xmin\sseq@infinity\sseq@error{no-range}\def\sseq@xmin{0}\def\sseq@xmax{0}\@xp\sseq@break\fi % could be warning
+ \ifx\sseq@ymin\sseq@infinity\sseq@error{no-range}\def\sseq@ymin{0}\def\sseq@ymax{0}\@xp\sseq@break\fi
+ \let\xmin\sseq@xmin
+ \let\xmax\sseq@xmax
+ \let\ymin\sseq@ymin
+ \let\ymax\sseq@ymax
+%
+ \ifnum\numexpr\sseq@xmax-\sseq@xmin\relax>\numexpr\maxdimen/\dimexpr1pt\relax-1\relax
+ \sseq@error@xx{range-super-overflow}{x}
+ {from \sseq@xmin\space to \sseq@xmax\space (xmax - xmin = \the\numexpr\sseq@xmax-\sseq@xmin)}
+ \@xp\sseq@break
+ \fi
+ \ifnum\numexpr\sseq@ymax-\sseq@ymin\relax>\numexpr\maxdimen/\dimexpr1pt\relax-1\relax
+ \sseq@error@xx{range-super-overflow}{y}
+ {from \sseq@ymin\space to \sseq@ymax\space (ymax - ymin = \the\numexpr\sseq@ymax-\sseq@ymin)}
+ \@xp\sseq@break
+ \fi
+%
+ \sseq@setlayoutparameters
+ \ifsseq@rangecheck
+ % Calculate available space
+
+ % We might need to know the dimensions of the tick marks in order to calculate "margin" size.
+ \bgroup
+ \@xp\tikzset\@xp{\sseq@xtickstyle}
+ \pgfmathparse{height("\sseq@xmin") + depth("\sseq@xmin")}
+ \edef\sseq@xlabelheight{\pgfmathresult pt}
+ \sseq@smuggle@macro\sseq@xlabelheight
+ \egroup
+ \bgroup
+ \@xp\tikzset\@xp{\sseq@ytickstyle}
+ \pgfmathparse{max(width("\sseq@ymax"), width("\sseq@ymin"))}
+ \edef\sseq@maxylabelwidth{\pgfmathresult pt}
+ \sseq@smuggle@macro\sseq@maxylabelwidth
+ \egroup
+
+ % "totaldirmargin" isn't really quite margin: it's the distance between
+ % the bounding box (xmin, ymin) -- (xmax, ymax) and the bounding box of
+ % the resulting figure.
+%
+ % By subtracting the "margin" distance from the page width / height, we
+ % get the amount of space available for the content.
+ \sseq@tempxdimen = \dimexpr \linewidth - \sseq@totalhorizontalmargin@yborder - 0.1pt
+ \sseq@tempydimen = \dimexpr \textheight - \sseq@totalverticalmargin@xborder - 0.1pt
+%
+ \ifsseq@rangecheck@sideways
+ % Swap availability measurements
+ \sseq@tempdimen=\sseq@tempxdimen
+ \sseq@tempxdimen=\sseq@tempydimen
+ \sseq@tempydimen=\sseq@tempdimen
+ \fi
+%
+ % \showthe\dimexpr\dimexpr \sseq@tempxdimen - \sseq@xscale cm * (\sseq@xmax-\sseq@xmin)\relax
+ % Compare space available to range * scale.
+ \ifdim \sseq@tempxdimen < \dimexpr \sseq@xscale cm * (\sseq@xmax-\sseq@xmin)\relax
+ % this will divide by zero if somehow we overflow the space on page
+ % while the range is 0. Hopefully no one will ever manage that.
+ \pgfmathparse{\sseq@tempxdimen/1cm/(\sseq@xmax-\sseq@xmin)} % max x range
+ \sseq@tempx = \numexpr \sseq@tempxdimen /\sseq@xscalecm \relax % max xscale
+ \sseq@error@xxxx{range-overflow}{x}
+ {from \sseq@xmin\space to \sseq@xmax\space (xmax - xmin = \the\numexpr\sseq@xmax-\sseq@xmin)}
+ {\the\sseq@tempx}% max x range
+ {\pgfmathresult}% max xscale
+ %\@xp\sseq@break
+ \fi
+%
+ \relax % Not sure why we need this...
+%
+ % \showthe\dimexpr\dimexpr \sseq@tempydimen - \sseq@yscale cm * (\sseq@ymax-\sseq@ymin)\relax
+ \ifdim \sseq@tempydimen < \dimexpr \sseq@yscale cm * (\sseq@ymax-\sseq@ymin)\relax
+ \sseq@tempy = \numexpr \sseq@tempydimen /\sseq@yscalecm \relax % max y range
+ \pgfmathparse{\sseq@tempydimen/1cm/(\sseq@ymax-\sseq@ymin)} % max yscale
+ \sseq@error@xxxx{range-overflow}{y}
+ {from \sseq@ymin\space to \sseq@ymax\space (ymax - ymin = \the\numexpr\sseq@ymax-\sseq@ymin)}
+ {\the\sseq@tempy}% max y range
+ {\pgfmathresult} % max yscale
+ %\@xp\sseq@break
+ \fi
+ \fi
+%
+ % Really this could be anything between -\sseq@xmin and -\sseq@xmax and it wouldn't matter. We'll split the difference.
+ \sseq@xoffset=\numexpr\sseq@intdivceiling{\numexpr-\sseq@xmin-\sseq@xmax\relax}{\numexpr4*\sseq@xgridstep\relax}*(2*\sseq@xgridstep)\relax
+ \sseq@yoffset=\numexpr\sseq@intdivceiling{\numexpr-\sseq@ymin-\sseq@ymax\relax}{\numexpr4*\sseq@ygridstep\relax}*(2*\sseq@ygridstep)\relax
+%
+%
+ \def\sseq@temp{\sseq@novalue}
+ \ifx\sseq@printpageas\sseq@temp
+ \def\sseq@printpageas{Page $2$ (all differentials)}%
+ \fi
+%
+%
+ \tikzset{
+ z=0pt,
+ every path/.style={},
+ every node/.style={}
+ }
+ \let\errmessage\sseq@errmessage@std
+ \begin{tikzpicture}[/sseqpages/global/{default} options,\ifsseq@hasname/sseqpages/global/{\sseq@thename} options\fi,/sseqpages/global/{this page} options]%
+ \sseq@sseqstyle@globalpart
+ \sseq@globaldetonetransform
+ \sseq@globalscaletransform
+%
+ \let\sseq@transform\sseq@error@illegaltransform % Disallow most coordinate transforms
+ \let\sseq@shifttransform\sseq@checkshifttransform % Allow shifts as long as they are by integers and have no units.
+%
+ \sseq@setupclip
+%
+ \sseq@patchtikzcoords
+%
+ \pgfonlayer{background}%
+ % Axes labels only occur once per page, so the cost of typesetting them with tikz doesn't matter ... THANK GOD
+ \ifx\sseq@title\pgfutil@empty\else
+ \ifsseq@drawtitle
+ \sseq@eval{\@nx\node[
+ /sseqpages/global/every title,
+ above=\sseq@topclippadding+5pt
+ ] {\unexpanded\expandafter{\sseq@title}};
+ }
+ \fi
+ \fi
+ % Don't touch the shifts -- they are very persnickety.
+ \ifx\sseq@xaxislabel\pgfutil@empty\else\ifsseq@drawxaxislabel
+ \sseq@eval{\noexpand\node[
+ align=center,
+ /sseqpages/global/every x axis label,
+ below,
+ shift={\sseq@xlabelposition},
+ ] {\unexpanded\expandafter{\sseq@xaxislabel}};}
+ \fi\fi
+ \ifx\sseq@yaxislabel\pgfutil@empty\else\ifsseq@drawyaxislabel
+ \sseq@eval{\noexpand\node[
+ align=center,
+ /sseqpages/global/every y axis label,
+ above,
+ rotate=90,
+ shift={\sseq@ylabelposition},
+ ] {\unexpanded\expandafter{\sseq@yaxislabel}};}
+ \fi\fi
+%
+ \sseq@grid
+ \sseq@handlexaxis % Draw the axis with pgf primitives. These are defined in sseqdrawing.code.tex
+ \sseq@handleyaxis
+ \endpgfonlayer
+%
+ \ifsseq@mathnodes
+ \tikzset{execute at begin node=$,execute at end node=$}%
+ \fi
+%
+ \begingroup
+ \sseq@savedbackgroundpaths
+ \endgroup
+%
+ \sseq@useclip
+%
+ \ifsseq@drawdifferentials\else
+ \let\sseq@differential@object\@gobble
+ \fi
+ \ifsseq@drawstructlines\else
+ \let\sseq@structline@object\@gobble
+ \fi
+%
+ \sseq@usesavedpaths
+%
+ \ifsseq@keepchanges
+ \xdef\sseq@restorestyles{\sseq@stylelist}
+ \fi
+ \end{tikzpicture}%
+ \ifsseq@keepchanges
+ \sseq@storecmds % store changes
+ \else
+ \sseq@pagecleanup % undo any changes specific to this page
+ \fi
+ \sseq@breakpoint
+ \ignorespacesafterend
+}
+
+% Shorthand for sseqpage with empty body
+\def\printpage[#1]{%
+ \begin{sseqpage}[#1]%
+ \end{sseqpage}%
+}
+
+
+\def\SseqCopyPage#1#2#3{
+ \begingroup
+ \def#1{\begingroup}
+ \global\let\sseq@copymacro\empty
+ \def\sseq@thename{#2}
+ \global\let\sseq@savedpaths@save\sseq@savedpaths
+ \sseq@storelist@get\sseq@savedpaths
+ \def\sseq@thepage{#3}
+ \sseq@thepagecount=#3\relax
+ \def\sseq@beginscope@object{\sseq@g@addto@macro#1{\sseq@savedpaths@add{\sseq@beginscope@object}}}
+ \def\sseq@endscope@object{\sseq@g@addto@macro#1{\sseq@savedpaths@add{\sseq@endscope@object}}}
+ \def\sseq@scope@object##1{\sseq@g@addto@macro#1{\sseq@savedpaths@add{\sseq@scope@object{##1}}}}
+ \def\sseq@style@object##1{\sseq@g@addto@macro#1{\sseq@savedpaths@add{\sseq@style@object{##1}}}}
+ \let\sseq@class@drawnode\sseq@copypage@addclass
+ \let\sseq@differential@object\@gobble
+ \let\sseq@structline@draw\sseq@copypage@addstructline
+ \let\sseq@extension@draw\sseq@copypage@addextension
+ \let\sseq@tikzpath@object\@gobble
+ \let\sseq@pgfkeysdocommand\@gobbletwo
+ \sseq@savedpaths
+ \sseq@g@addto@macro#1{\endgroup}
+ \endgroup
+ \@xp\let\csname\sseq@macroname#1 copiedcode\endcsname\sseq@copymacro
+ \DeclareSseqGroup #1 {} { \csname\sseq@macroname#1 copiedcode\endcsname }
+ \let\sseq@savedpaths\sseq@savedpaths@save
+}
+
+\def\sseq@copypage@addclass#1{
+ \sseq@x@addto@macro\sseq@copymacro{\@nx\sseq@class@copy{\sseq@thename}{#1}}
+}
+
+\def\sseq@copypage@addstructline#1{
+ \sseq@x@addto@macro\sseq@copymacro{\@nx\sseq@structline@copy{\sseq@thename}{#1}}
+}
+
+\def\sseq@copypage@addextension#1{
+ \sseq@x@addto@macro\sseq@copymacro{\@nx\sseq@extension@copy{\sseq@thename}{#1}}
+}
+
+
+%%% Scope
+\newcount\sseq@scope@savestackid
+\def\sseq@scope@secondpasstoks{}
+\def\sseq@scope{\@ifnextchar[{\sseq@scope@}{\sseq@scope@[]}}%]
+\def\sseq@scope@[#1]{%
+ \sseq@scope@savedpaths@add{\sseq@beginscope@object}%
+ % This is a funky hack to fix a problem with shifts. On tmfass, if we don't handle shifts specially, we actually put so much stuff into
+ % this \sseq@scope@secondpasstoks macro that we exceed TeX's upper bound for the maximum total macro memory. Or something like that -- I don't understand the error,
+ % but it definitely only depends on how much stuff there is overall and on there being lots of scopes, not on any particular part of the diagram.
+ % Anyways, to avoid this excessive storage demand, we don't put the shifts into the scope toks and apply them tons of times, we just do them once.
+ \let\sseq@options@scopeshiftsecondpass\sseq@scope@savedpaths@add
+ \sseq@processoptions{scope}{#1}% outputs into \sseq@savedoptioncode
+ \edef\sseq@scope@secondpasstoks{\unexpanded\@xp{\sseq@scope@secondpasstoks}\unexpanded\@xp{\sseq@savedoptioncode}}
+ \sseq@eval{\@nx\sseq@scope@savedpaths@add{\@nx\sseq@scope@object{\sseq@scope@toks{\unexpanded\@xp{\sseq@scope@secondpasstoks}}}}}%
+ %\advance\sseq@scope@savestackid\@ne
+ %\sseq@savestack@name{scope@save\the\sseq@scope@savestackid}
+}
+\def\sseq@endscope{%
+ \sseq@scope@savedpaths@add{\sseq@endscope@object}
+ %\sseq@restorestack@name{scope@save\the\sseq@scope@savestackid
+}
+
+
+
+
+
+
+
+\protected\def\sseq@xcoord{}\protected\def\sseq@ycoord{}
+
+
+%%% "Quick" commands for "compiled" code.
+\def\sseq@qclass(#1,#2){
+ \sseq@obj@inccheckdef{partcoord.(#1,#2).numnodes}
+ \edef\tempn{\sseq@obj{partcoord.(#1,#2).numnodes}}
+ \edef\classname{class.(#1,#2,\tempn)}
+ \sseq@obj@xdef{\classname.n}{\tempn}
+ \sseq@obj@xdef{\classname.num}{0}
+ \sseq@obj@xdef{\classname[0].page}{\sseq@infinity}
+ \sseq@savedpaths@addclass{class.(#1,#2,\tempn)}
+}
+\def\sseq@qclassnamed(#1,#2)#3{
+ \sseq@qclass(#1,#2)
+ \def\sseq@class@name{#3}
+ \sseq@obj@xdef{\classname[0].name}{\sseq@class@name}
+ \sseq@obj@xdef{class.namedclass.\detokenize\@xp{\sseq@class@name}}{#1,#2,\tempn}
+}
+
+\def\sseq@qdnamed#1(#2)(#3){\sseq@eval{\@nx\sseq@qd#1(\sseq@obj{class.namedclass.\detokenize{#2}})(\sseq@obj{class.namedclass.\detokenize{#3}})}}
+\def\sseq@qd#1(#2,#3,#4)(#5,#6,#7){
+ \bgroup
+ \def\sseq@dname{d.#1(#2,#3,#4)(#5,#6,#7)}
+ \def\source{(#2,#3,#4)}
+ \def\target{(#5,#6,#7)}
+ \sseq@obj@gdef{\sseq@dname.page}{#1}
+ \sseq@obj@gdef{\sseq@dname.source}{(#2,#3,#4)}
+ \sseq@obj@gdef{\sseq@dname.target}{(#5,#6,#7)}
+ \sseq@d@setpageminrec{class.\source}{#1}{source}% automatically handles \sseq@cleanup@obj
+ \sseq@d@setpageminrec{class.\target}{#1}{target}
+ \sseq@savedpaths@adddifferential{\sseq@dname}
+ \egroup
+}
+
+\def\sseq@qstructlinenamed(#1)(#2){\sseq@eval{\@nx\sseq@qstructline(\sseq@obj{class.namedclass.\detokenize{#1}})(\sseq@obj{class.namedclass.\detokenize{#2}})}}
+\def\sseq@qstructline(#1,#2,#3)(#4,#5,#6){
+ \def\source{(#1,#2,#3)}
+ \def\target{(#4,#5,#6)}
+ \sseq@obj@xdef{structline.\source\target.num}{0}
+ \def\sseq@pagemin{0}
+ \edef\sseq@structlinename{structline.\source\target[\sseq@obj{structline.\source\target.num}]}
+ \sseq@cleanup@obj{\sseq@structlinename.page}
+ \sseq@obj@xdef{\sseq@structlinename.page}{\sseq@infinity}
+ \sseq@obj@xdef{\sseq@structlinename.pagemin}{\sseq@pagemin}
+ \sseq@obj@xdef{\sseq@structlinename.source}{\source}
+ \sseq@obj@xdef{\sseq@structlinename.target}{\target}
+ \sseq@class@adddependence{class.\source}{\sseq@structlinename}% If the source or target is already dead, this will set structlinename.page
+ \sseq@class@adddependence{class.\target}{\sseq@structlinename}
+ \sseq@savedpaths@addstructline{structline.\source\target}
+ %\sseq@cleanup@obj{\sseq@structlinename.options}
+ %\sseq@obj@xdef{\sseq@structlinename.options}{\ifsseq@needstikz\@nx\sseq@needstikztrue\fi\unexpanded\@xp{\sseq@savedoptioncode}}
+}
+
+
+
+%%%%%
+%%
+%% The main commands
+%%
+%%%%%
+%%% \class, \classoptions, \replaceclass
+\sseq@DeclareDocumentCommand\class{or()}{%
+ \begingroup
+ \sseq@loadinputline
+ \IfNoValueT{#2}{\sseq@break}
+ \sseq@xsetthiscall{\string\class\IfNoValueF{#1}{[\unexpanded{#1}]}\unexpanded{(#2)}}%
+ \sseq@options@firstpassmode
+ \the\sseq@sseqstyle
+ \the\sseq@classstyle
+ \the\sseq@scope@toks
+ \sseq@options@normalmode
+ \IfNoValueF{#1}{%
+ \sseq@processoptions{class}{#1}% Outputs result into \sseq@savedoptioncode
+ }%
+%
+ \@ifundefined{sseq@class@page}{}{\sseq@error@xxx{cmdoptions-only}{page} {\string\class}{\string\classoptions}}% could be warnings
+ \@ifundefined{sseq@gen} {}{\sseq@error@xxx{cmdoptions-only}{generation}{\string\class}{\string\classoptions}}
+%
+ \sseq@cparsecoord{#2}{}%
+ \ifnum\csname sseq@\sseq@classpattern @maxclasses\endcsname<\nodenum\relax
+ \sseq@error@xxxx{classpattern-too-many-classes}{(\partialcoord)}{\nodenum}{\sseq@classpattern}{\@xp\the\csname sseq@\sseq@classpattern @maxclasses\endcsname}
+ \sseq@breakfi
+ \fi
+%%%% No more breaks after this point
+
+ \sseq@processlabels % This sets up the label nodes, now that we know the position of the class (I think that's what we needed?)
+ \sseq@class@main % We segment off the main part of the class so that we can call it separately from the copy method
+ \sseq@breakpoint
+ \endgroup
+}
+%
+\def\sseq@class@main{
+ \ifx\sseq@classinsert\empty
+ % by default, just insert to the right of previous classes
+ \edef\sseq@classinsert{\nodenum}
+ \else
+ \ifnum\sseq@classinsert<\z@
+ % negative 1 is where we would normally insert things
+ \edef\sseq@classinsert{\the\numexpr\nodenum+\sseq@classinsert+1}
+ \fi
+ \ifnum\sseq@classinsert<\@ne
+ \def\sseq@classinsert{1}
+ \fi
+ \ifnum\sseq@classinsert>\nodenum\relax
+ \edef\sseq@classinsert{\nodenum}
+ \fi
+ \ifnum\nodenum>\@ne
+ \@xp\sseq@class@adjustclasses\@xp{\partialcoord} % Adjust all of the indices of the classes to the right of the one we're inserting
+ \fi
+ \fi
+%
+%
+ \sseq@xminmax{\xcoord}{\ycoord}%
+ \sseq@yminmax{\xcoord}{\ycoord}%
+ \sseq@cleanup@obj{partcoord.(\partialcoord).numnodes}%
+ \sseq@obj@xdef{partcoord.(\partialcoord).numnodes}{\nodenum}%
+ \edef\classname{class.\coord[0]}%
+ \ifx\sseq@class@tag\pgfutil@empty\else
+ \sseq@obj@ifdef{partcoord.(\partialcoord).tag.\sseq@class@tagprefix\sseq@class@tag}{
+ \sseq@error@xx{class-tag-already-defined}{\sseq@class@tagprefix\sseq@class@tag}{(\partialcoord)}% could be warning
+ }{}
+ \sseq@cleanup@obj{class.\coord.tag}
+ \sseq@obj@xdef{class.\coord.tag}{\sseq@class@tagprefix\sseq@class@tag}
+ \sseq@cleanup@obj{partcoord.(\partialcoord).tag.\sseq@class@tagprefix\sseq@class@tag}
+ \sseq@obj@xdef{partcoord.(\partialcoord).tag.\sseq@class@tagprefix\sseq@class@tag}{\nodenum}%
+ \fi
+ \ifx\sseq@class@name\pgfutil@empty\else
+ \sseq@cleanup@obj{\classname.name}
+ \sseq@cleanup@obj{class.namedclass.\detokenize\@xp{\sseq@class@name}}
+ \sseq@obj@xdef{\classname.name}{\sseq@class@name}
+ \sseq@obj@xdef{class.namedclass.\detokenize\@xp{\sseq@class@name}}{\coordnopar}
+ \fi
+ \sseq@pushstack@\coord
+%
+%
+ \sseq@obj@gdef{class.\coord.num}{0}% For \replaceclass
+ \sseq@obj@xdef{\classname.page}{\sseq@infinity}%
+ \sseq@obj@xdef{class.\coord.n}{\sseq@classinsert}
+ \ifx\sseq@familylist\pgfutil@empty\else
+ \sseq@obj@xodef{\classname.familylist}{\sseq@familylist}
+ \fi
+ \ifx\sseq@savedoptioncode\pgfutil@empty\else
+ \sseq@obj@xodef{\classname.options}{\sseq@savedoptioncode}%
+ \fi
+ \ifx\sseq@classnodetext\pgfutil@empty\else
+ \sseq@obj@xodef{\classname.nodetext}{\sseq@classnodetext}
+ \fi
+ \ifx\sseq@classnodetextoptions\pgfutil@empty\else
+ \sseq@obj@xodef{\classname.nodetext.options}{\sseq@classnodetextoptions}
+ \fi
+ \ifx\sseq@classlabelnodes\pgfutil@empty\else
+ \sseq@obj@xodef{\classname.labelnodes}{\sseq@classlabelnodes}%
+ \fi
+ \ifcsname sseq@class@showname\endcsname
+ \sseq@obj@xodef{\classname.showname}{\sseq@class@showname}
+ \fi
+ \ifsseq@needstikz
+ \sseq@obj@xdef{\classname.needstikz}{\sseq@needstikztrue}%
+ \fi
+ \@ifundefined{sseq@tempoffset}{}{%
+ \@xp\tikz@scan@one@point\@xp\sseq@setoffset\sseq@tempoffset
+ }%
+ \ifx\sseq@tooltip\pgfutil@empty\else
+ \sseq@obj@protectedxdef{\classname.tooltip}{\sseq@tooltip}
+ \fi
+%
+ \sseq@savedpaths@addclass{class.\coord}%
+}
+\def\sseq@setoffset#1{\sseq@obj@gdef{\classname.offset}{\pgftransformshift{#1}}}
+
+\def\sseq@copy@nameprefix{}
+
+\def\sseq@class@copy#1#2{
+ \begingroup
+ \let\sseq@thename@saved\sseq@thename
+ \def\sseq@thename{#1}
+ \sseq@lettoobj\sseq@familylist{#2.familylist}
+ \sseq@lettoobj\sseq@savedoptioncode{#2.options}
+ \sseq@lettoobj\sseq@classnodetext{#2.nodetext}
+ \sseq@lettoobj\sseq@classnodetextoptions{#2.nodetext.options}
+ \sseq@lettoobj\sseq@classlabelnodes{#2.labelnodes}
+ \sseq@obj@ifdef{#2.showname}{\sseq@lettoobj\sseq@class@showname{#2.showname}}{}
+ \sseq@lettoobj\sseq@class@name{#2.name}
+ \edef\sseq@class@name{\unexpanded\@xp{\sseq@copy@nameprefix}\unexpanded\@xp{\sseq@class@name}}
+ \sseq@obj{#2.needstikz}
+ \sseq@lettoobj\sseq@tooltip{#2.tooltip}
+ \sseq@class@getparts#2.
+ \sseq@quieterror{class-tag-already-defined}
+ \edef\sseq@class@tag{copiedfrom\sseq@thisposnum}
+ \let\sseq@thename\sseq@thename@saved
+ \sseq@eval{\@nx\sseq@cparsecoord{\the\sseq@tempx,\the\sseq@tempy}}
+ \let\sseq@pushstack@\@gobble
+ \sseq@class@main
+ \endgroup
+}
+
+
+% This is for use with "insert" key. Add one to offset index of each class later than the one currently being inserted
+% Takes the partial coordinate "x,y" in #1, and the position that the new class is being inserted into in \sseq@classinsert.
+\def\sseq@class@adjustclasses#1{
+ \sseq@tempcount=\z@
+ \sseq@tempcountb=\sseq@obj{partcoord.(#1).numnodes}
+ \loop
+ \advance\sseq@tempcount\@ne
+ \ifnum\sseq@obj{class.(#1,\the\sseq@tempcount).n}<\sseq@classinsert\relax\else
+ \sseq@cleanup@obj{class.(#1,\the\sseq@tempcount).n}
+ \sseq@eval{\@nx\sseq@obj@inc{class.(#1,\the\sseq@tempcount).n}} % unfortunately, \sseq@obj@inc uses \sseq@tempcount so we need this eval
+ \fi
+ \ifnum\sseq@tempcount<\sseq@tempcountb\repeat
+
+}
+
+\sseq@DeclareDocumentCommand\classoptions{od()}{%
+ \begingroup
+ \sseq@loadinputline
+ \sseq@xsetthiscall{\string\classoptions\IfNoValueF{#1}{[\unexpanded{#1}]}\unexpanded{(#2)}}%
+ \IfNoValueT{#1}{\sseq@error@x{cmdoptions-without-options}{\string\classoptions}\sseq@break}% could be warning
+ \sseq@options@firstpassmode
+ \the\sseq@sseqstyle
+ \the\sseq@classstyle
+ \the\sseq@scope@toks
+ \sseq@options@normalmode
+ \let\sseq@processlabels\sseq@processlabels@default % Don't double dip on labels from the scope or classstyle
+ \sseq@processoptions{class}{#1}% Outputs result into \sseq@savedoptioncode
+%
+ \IfNoValueTF{#2}{
+ \def\sseq@coord{\lastclass0}
+ }{
+ \def\sseq@coord{#2}
+ }
+ \sseq@parsecoord\coord{\sseq@coord}{\string\classoptions}%
+%
+ \sseq@processlabels
+%
+ \let\partialcoord\coord@partial
+ \edef\sseq@classnum{\sseq@obj@ifdef{class.\coord.num}{\sseq@obj{class.\coord.num}}{0}}
+ \@ifundefined{sseq@class@page}{%
+ \@ifundefined{sseq@gen}{
+ \@ifundefined{sseq@thepage}{
+ \let\sseq@gen\sseq@classnum
+ }{
+ \sseq@obj@pagetogen{class.\coord}{\sseq@thepage} % stores in \sseq@gen
+ }
+ \let\sseq@genmax\sseq@gen
+ }{
+ \ifnum\sseq@gen<\z@
+ \edef\sseq@gen{\the\numexpr\sseq@classnum+\sseq@gen+1}
+ \fi
+ \@ifundefined{sseq@genmax}{\let\sseq@genmax\sseq@gen}{
+ \edef\sseq@genmax{\the\numexpr\sseq@classnum+\sseq@genmax+1}
+ }
+ }
+ }{
+ \@ifundefined{sseq@gen}{}{\sseq@error{classoptions-page-gen}}% This could be a warning
+ \sseq@class@getgen{\sseq@class@page}
+ \@ifundefined{sseq@class@pagemax}{
+ % If no max is present, apply options to all pages starting at specified minimum
+ \let\sseq@genmax\sseq@classnum%
+ \let\sseq@class@pagemax\sseq@infinity % ensure if "page" is defined, "pagemax" is defined too
+ }{
+ \let\sseq@genmin\sseq@gen
+ \sseq@class@getgen{\sseq@class@pagemax}
+ \let\sseq@genmax\sseq@gen
+ \let\sseq@gen\sseq@genmin
+ }
+ \edef\sseq@class@page{\the\numexpr\sseq@class@page-\@ne}
+ \edef\sseq@class@pagemax{\the\numexpr\sseq@class@pagemax+\@ne}
+ }
+ \ifnum\sseq@gen=\m@ne
+ \let\sseq@gen\sseq@classnum
+ \fi
+ \edef\classname{class.\coord[\sseq@gen]}
+ \ifsseq@keepchanges\else
+ \ifnum\sseq@thepagecount>\sseq@obj{\classname.page}\relax
+ \sseq@error@xxx{classoptions-already-dead}{\sseq@gen}{\coordname}{\sseq@obj{\classname.page}}
+ \sseq@breakfifi
+ \fi
+ \ifnum\sseq@gen>\z@
+ \ifnum\sseq@thepagecount>\sseq@obj{class.\coord[\the\numexpr\sseq@gen-\@ne].page}\relax\else
+ \sseq@error@xx{classoptions-not-yet-born}{\sseq@gen}{\coord}{\sseq@class@page}%
+ \sseq@breakfififi
+ \fi
+ \fi
+ \fi{}
+ \ifx\sseq@class@tag\pgfutil@empty\else
+ \sseq@obj@ifdef{partcoord.(\partialcoord).tag.\sseq@class@tagprefix\sseq@class@tag}{%
+ \sseq@error@nn{class-tag-already-defined}{\sseq@class@tagprefix\sseq@class@tag}{\sseq@partialcoord}% could be warning
+ }%
+ \sseq@cleanup@obj{class.\coord.tag}%
+ \sseq@obj@xdef{class.\coord.tag}{\sseq@class@tagprefix\sseq@class@tag}%
+ \sseq@cleanup@obj{partcoord.(\partialcoord).tag.\sseq@class@tagprefix\sseq@class@tag}%
+ \sseq@obj@xdef{partcoord.(\partialcoord).tag.\sseq@class@tagprefix\sseq@class@tag}{\sseq@index}%
+ \fi
+ \ifx\sseq@class@name\pgfutil@empty\else
+ \sseq@obj@ifdef{\classname.name}{
+ \sseq@error@nn{classoptions-class-already-named}{\coord}{\sseq@obj{\classname.name}}%could be warning
+ }{}
+ \sseq@cleanup@obj{\classname.name}
+ \sseq@cleanup@obj{class.namedclass.\detokenize\@xp{\sseq@class@name}}
+ \sseq@obj@xdef{\classname.name}{\sseq@class@name}
+ \sseq@obj@xdef{class.namedclass.\detokenize\@xp{\sseq@class@name}}{\coordnopar}
+ \fi
+ \sseq@tempcount=\sseq@gen\relax
+ \advance\sseq@tempcount\m@ne
+ \loop
+ \advance\sseq@tempcount\@ne
+ \edef\classname{class.\coord[\the\sseq@tempcount]}
+ \sseq@cleanup@obj{\classname.options}
+ \sseq@classoptions@setup@pagerange
+ \sseq@classoptions@obj@addto@withpagerange{\classname.options}{\sseq@savedoptioncode}
+ %
+ \ifx\sseq@classnodetext\pgfutil@empty\else
+ \sseq@cleanup@obj{\classname.nodetext}
+ \@ifundefined{sseq@class@page}{}{
+ \sseq@obj@xdef{\classname.nodetext}{
+ \@nx\ifnum\@nx\sseq@thepagecount<\the\numexpr\sseq@class@page+1\relax\relax
+ \unexpanded\@xptwo{\csname \sseq@obj@fullname{\classname.nodetext}\endcsname\fi}
+ }
+ }
+ \sseq@classoptions@obj@addto@withpagerange{\classname.nodetext}{\sseq@classnodetext}
+ \fi
+ \ifx\sseq@classnodetextoptions\pgfutil@empty\else
+ \sseq@cleanup@obj{\classname.nodetext.options}
+ \sseq@classoptions@obj@addto@withpagerange{\classname.nodetext.options}{\sseq@classnodetextoptions}
+ \fi
+ \ifx\sseq@classlabelnodes\pgfutil@empty\else
+ \sseq@cleanup@obj{\classname.labelnodes}
+ \sseq@classoptions@obj@addto@withpagerange{\classname.labelnodes}{\sseq@classlabelnodes}
+ \fi
+ \ifcsname sseq@class@showname\endcsname
+ \sseq@obj@xodef{\classname.showname}{\sseq@class@showname}
+ \fi
+ \ifx\sseq@familylist\pgfutil@empty\else
+ \sseq@cleanup@obj{\classname.familylist}
+ \sseq@classoptions@obj@addto@withpagerange{\classname.familylist}{\sseq@familylist}
+ \fi
+ \@ifundefined{sseq@tempoffset}{}{
+ \sseq@cleanup@obj{\classname.offset}
+ \@xp\tikz@scan@one@point\@xp\sseq@setoffset\sseq@tempoffset
+ }
+ \ifnum\sseq@tempcount<\sseq@genmax\repeat
+ \sseq@breakpoint
+ \endgroup
+}
+\def\sseq@classoptions@setup@pagerange{%
+ \@ifundefined{sseq@class@page}{
+ \def\sseq@classoptions@obj@addto@withpagerange@{\sseq@obj@gaddto{\sseq@tempobj}}
+ }{
+ \def\sseq@temp##1{##1}
+ \ifnum\sseq@tempcount=\sseq@gen\relax
+ \sseq@eval{\@nx\pretocmd\@nx\sseq@temp{\@nx\ifnum\sseq@thepagecount>\sseq@class@page\relax}{}{%
+ \@nx\sseq@error@x{this-shouldnt-happen}{file main, function \string\sseq@classoptions@setup@pagerange}
+ }}%
+ \apptocmd\sseq@temp{\fi}{}{\sseq@error@x{this-shouldnt-happen}{file main, function \string\sseq@classoptions@setup@pagerange}}
+ \fi
+ \ifnum\sseq@tempcount=\sseq@genmax\relax
+ \sseq@eval{\@nx\pretocmd\@nx\sseq@temp{\@nx\ifnum\sseq@thepagecount<\sseq@class@pagemax\relax}{}{\@nx\sseq@error@internal{}}}
+ \apptocmd\sseq@temp{\fi}{}{\sseq@error@internal{this-shouldnt-happen}{file main, function \string\sseq@classoptions@setup@pagerange}}
+ \fi
+ \@xp\def\@xp\sseq@temp\@xp##\@xp1\@xp{\@xp{\sseq@temp{##1}}}
+ \pretocmd\sseq@temp{\sseq@obj@gaddto{\sseq@tempobj}}{}{\sseq@error@internal{this-shouldnt-happen}{file main, function \string\sseq@classoptions@setup@pagerange}}
+ \let\sseq@classoptions@obj@addto@withpagerange@\sseq@temp
+ }
+}
+\def\sseq@classoptions@obj@addto@withpagerange#1#2{%
+ \def\sseq@tempobj{#1}%
+ \@xp\sseq@classoptions@obj@addto@withpagerange@\@xp{#2}%
+}
+\def\sseq@class@getgen#1{
+ \bgroup
+ \ifnum\sseq@classnum>\z@
+ \sseq@eval{\unexpanded{\sseq@obj@pagetogen{class.\coord}}{#1}} % stores in \sseq@gen
+ \else
+ \def\sseq@gen{0}
+ \fi
+ \sseq@smuggle@macro\sseq@gen
+ \egroup
+}
+
+\sseq@DeclareDocumentCommand\replaceclass{od()}{%
+ \begingroup
+ \sseq@loadinputline
+ \sseq@xsetthiscall{\string\replaceclass\IfNoValueF{#1}{[\unexpanded{#1}]}\unexpanded{(#2)}}
+%
+ \IfNoValueTF{#2}{
+ \def\sseq@coord{\lastclass0}
+ }{
+ \def\sseq@coord{#2}
+ }
+ \sseq@parsecoord\coord{\sseq@coord}{coordinate passed to \string\replaceclass}%
+ \sseq@replaceclassmain{#1}
+}
+
+\sseq@DeclareDocumentCommand\replacesource{o}{
+ \begingroup
+ \sseq@loadinputline
+ \sseq@xsetthiscall{\string\replacesource\IfNoValueF{#1}{[\unexpanded{#1}]}}
+ \sseq@parsedcoord@restore{\coord}{lastsource}
+ \sseq@replaceclassmain{#1}
+}
+
+\sseq@DeclareDocumentCommand\replacetarget{o}{
+ \begingroup
+ \sseq@loadinputline
+ \sseq@xsetthiscall{\string\replacetarget\IfNoValueF{#1}{[\unexpanded{#1}]}}
+ \sseq@parsedcoord@restore{\coord}{lasttarget}
+ \sseq@replaceclassmain{#1}
+}
+
+\def\sseq@replaceclassmain#1{
+ \sseq@pushstack@\coord
+ \sseq@options@firstpassmode
+ \the\sseq@sseqstyle
+ \the\sseq@classstyle
+ \the\sseq@scope@toks
+ \sseq@options@normalmode
+ \IfNoValueF{#1}{%
+ \sseq@processoptions{class}{#1}% Outputs result into \sseq@savedoptioncode
+ }%
+ \sseq@processlabels
+ \sseq@obj@ifundef{class.\coord.num}{\sseq@error@x{replaceclass-no-class}{\coordname}\sseq@break}{}
+ \sseq@ifdead\coord{}{\sseq@error@x{replaceclass-not-dead-yet}{\coordname}\sseq@break}
+ \edef\oldclassname{class.\coord[\sseq@obj{class.\coord.num}]}
+% \ifsseq@keepchanges\else
+% \ifnum\the\numexpr\sseq@obj{\oldclassname.page}<\sseq@thepagecount\else
+% \edef\sseq@temp{\sseq@obj{\oldclassname.page}}
+% \sseq@error@xxx{replaceclass-no-effect-on-this-page}{\coord}{\sseq@temp}{\the\numexpr\sseq@temp+\@ne}% warning
+% \sseq@breakfifi
+% \fi
+% \fi
+%
+ \sseq@cleanup@obj{class.\coord.num}
+ \sseq@obj@inc{class.\coord.num}
+ \edef\classname{class.\coord[\sseq@obj{class.\coord.num}]}
+ %\show\classname
+%
+ \let\partialcoord\coord@partial
+ \ifx\sseq@class@tag\pgfutil@empty\else
+ \sseq@obj@ifdef{partcoord.(\partialcoord).tag.\sseq@class@tagprefix\sseq@class@tag}{%
+ \sseq@error@nn{class-tag-already-defined}{\sseq@class@tagprefix\sseq@class@tag}{\sseq@partialcoord}% could be warning
+ }{}%
+ \sseq@cleanup@obj{partcoord.(\partialcoord).tag.\sseq@class@tagprefix\sseq@class@tag}%
+ \sseq@obj@xdef{partcoord.(\partialcoord).tag.\sseq@class@tagprefix\sseq@class@tag}{\nodenum}%
+ \fi
+ \ifx\sseq@class@name\pgfutil@empty
+ \sseq@obj@ifdef{\oldclassname.name}{
+ \sseq@lettoobj\sseq@class@name{\oldclassname.name}
+ \sseq@cleanup@obj{\classname.name}
+ \sseq@cleanup@obj{class.namedclass.\detokenize\@xp{\sseq@class@name}}
+ \sseq@obj@xdef{\classname.name}{\sseq@class@name}
+ \sseq@obj@xdef{class.namedclass.\detokenize\@xp{\sseq@class@name}}{\coordnopar}
+ }{}
+ \else
+ \sseq@cleanup@obj{\classname.name}
+ \sseq@cleanup@obj{class.namedclass.\detokenize\@xp{\sseq@class@name}}
+ \sseq@obj@xdef{\classname.name}{\sseq@class@name}
+ \sseq@obj@xdef{class.namedclass.\detokenize\@xp{\sseq@class@name}}{\coordnopar}
+ \fi
+ \let\partialcoord\coord@partial
+ \ifx\sseq@class@tag\pgfutil@empty\else
+ \sseq@obj@ifdef{partialcoord.(\partialcoord).tag.\sseq@class@tagprefix\sseq@class@tag}{%
+ \sseq@error@nn{class-tag-already-defined}{\sseq@class@tagprefix\sseq@class@tag}{\sseq@partialcoord}% could be warning
+ }{}%
+ \sseq@obj@xdef{partialcoord.(\partialcoord).tag.\sseq@class@tagprefix\sseq@class@tag}{\nodenum}%
+ \fi
+%
+ \ifcsname sseq@class@showname\endcsname
+ \sseq@obj@xodef{\classname.showname}{\sseq@class@showname}
+ \fi
+ \sseq@obj@xodef{\classname.familylist}{\sseq@familylist}
+ \sseq@obj@xdef{\classname.page}{\sseq@infinity}
+ \sseq@obj@xodef{\classname.options}{\sseq@savedoptioncode}
+ \ifx\sseq@classnodetext\pgfutil@empty\else
+ \sseq@obj@xodef{\classname.nodetext}{\sseq@classnodetext}
+ \fi
+ \ifx\sseq@classnodetextoptions\pgfutil@empty\else
+ \sseq@obj@xodef{\classname.nodetext.options}{\sseq@classnodetextoptions}
+ \fi
+ \sseq@obj@xodef{\classname.labelnodes}{\sseq@classlabelnodes}
+ \ifcsname sseq@class@showname\endcsname
+ \sseq@obj@xodef{\classname.showname}{\sseq@class@showname}
+ \fi
+ \sseq@obj@gdef{\classname.dependencies}{}
+ \ifsseq@needstikz
+ \sseq@obj@xdef{\classname.needstikz}{\sseq@needstikztrue}
+ \fi
+ \@ifundefined{sseq@tempoffset}{}{\@xp\tikz@scan@one@point\@xp\sseq@setoffset\sseq@tempoffset}
+%
+ \sseq@breakpoint
+ \endgroup
+}
+
+\sseq@DeclareDocumentCommand\replacestructlines{od()}{%
+ \begingroup
+ \sseq@loadinputline
+ \sseq@xsetthiscall{\string\replaceclass\IfNoValueF{#1}{[\unexpanded{#1}]}\unexpanded{(#2)}}
+%
+ \IfNoValueTF{#2}{
+ \def\sseq@coord{\lastclass0}
+ }{
+ \def\sseq@coord{#2}
+ }
+ \sseq@parsecoord\coord{\sseq@coord}{coordinate passed to \string\replaceclass}%
+%
+ % TODO: error handling here...
+%
+ \edef\sseq@classnum{\sseq@obj{class.\coord.num}}%
+ \ifnum\sseq@classnum=0\relax
+ \sseq@error@x{replacestructlines-class-not-replaced}{\coordname}\sseq@breakfi
+ \fi
+ \edef\classname{class.\coord[\sseq@classnum]}%
+ %\show\classname
+ \edef\oldclassname{class.\coord[\the\numexpr\sseq@obj{class.\coord.num}-1]}%
+ \sseq@cleanup@obj{\classname.dependencies}%
+ \sseq@obj@gletobj{\classname.dependencies}{\oldclassname.dependencies}%
+ %\sseq@obj@show{\classname.dependencies}
+ \sseq@obj@map{\classname.dependencies}{
+ \sseq@cleanup@obj{##1.page}%
+ \sseq@obj@gletobj{##1.page}{##1.page.old}%
+ }
+ \sseq@breakpoint
+ \endgroup
+}
+
+\def\sseq@class@draw@ifpage#1{
+ \bgroup
+ \sseq@obj@pagetogen{#1}{\sseq@thepage} % sets \sseq@gen
+ \ifnum\sseq@gen>\m@ne % -1 means no class to print
+ \ifnum\sseq@obj{#1[\sseq@gen].page}=\sseq@thepagecount
+ \sseq@thispagetrue
+ \fi
+ \sseq@drawtrue
+ \sseq@obj{#1[\sseq@gen].familylist}
+ \ifsseq@draw
+ \sseq@gsetthiscall{class #1}
+ \sseq@class@drawnode{#1[\sseq@gen]}
+ \fi
+ \fi
+ \egroup
+}
+
+% #1 -- object
+% #2 -- page
+% If there is a class #1 drawn on page #2, set \sseq@gen to be the generation of that class, otherwise set \sseq@gen to be -1.
+\def\sseq@obj@pagetogen#1#2{
+ \bgroup
+ \def\sseq@gen{-1}
+ \sseq@tempcount=\m@ne
+ \sseq@tempcountb=\sseq@obj{#1.num}\relax
+ \loop
+ \advance\sseq@tempcount\@ne
+ \ifnum\sseq@obj{#1[\the\sseq@tempcount].page}<#2\relax\else
+ \edef\sseq@gen{\the\sseq@tempcount}
+ \sseq@tempcount=\sseq@tempcountb\relax
+ \fi
+ \ifnum\sseq@tempcount<\sseq@tempcountb\repeat
+ \sseq@smuggle@macro\sseq@gen
+ \egroup
+}
+%%%
+%%% \kill, \d and \doptions
+%%%
+
+% Because \kill doesn't take options, there's no need to use \sseq@DeclareDocumentCommand for the outer command.
+% Rather than use \sseq@DeclareDocumentCommand with no arguments, we just do the normal stuff that \sseq@DeclareDocumentCommand would do,
+% Then call \sseq@d@grabpage, the command that is responsible for handling the strange syntax of the page argument for \d.
+\protected\def\sseq@kill{%
+ \begingroup
+ \sseq@setinputline % These two lines normally would happen inside of \sseq@DeclareDocumentCommand.
+ \sseq@loadinputline
+ \sseq@d@grabpage\sseq@kill@
+}
+
+
+% Now we need to parse a potential coordinate.
+\sseq@DeclareDocumentCommandAs\sseq@kill@\kill {d()} {
+ \sseq@xsetthiscall{\string\kill\unexpanded\@xp{\sseq@dpage}\IfNoValueF{#1}{(#1)}}%
+ \sseq@parsecoord\coordinate{\IfNoValueTF{#1}{\lastclass}{#1}}{}%
+ \sseq@parsedcoord@save{lastsource}{\coordinate}
+ \sseq@parsedcoord@save{lasttarget}{\coordinate}
+ \sseq@ifintexpr{\sseq@dpage}{%
+ \sseq@tempcount=\numexpr\sseq@dpage\relax % this is a convenient way to get rid of spaces because #3 has to be a number
+ }{%
+ \sseq@error@x{d-invalid-page}{\unexpanded\@xp{\sseq@dpage}}\sseq@break%
+ }%
+ \sseq@eval{\@nx\sseq@d@setpageminrec{class.\coordinate}{\the\sseq@tempcount}{coordinate}}% automatically handles \sseq@cleanup@obj
+ \sseq@breakpoint
+ \endgroup
+}
+
+% \sseq@d@grabpage is defined in sseqparsers.
+\sseq@DeclareDocumentCommand\d{o}{%
+ \begingroup
+ \sseq@loadinputline
+ \def\sseq@dtype{d}
+ \def\sseq@d@theoptions{#1}
+ \sseq@d@grabpage\sseq@d@
+}
+
+\sseq@DeclareDocumentCommand\doptions{o}{%
+ \begingroup
+ \sseq@loadinputline
+ \def\sseq@dtype{doptions}
+ \def\sseq@d@theoptions{#1}
+ \sseq@d@grabpage\sseq@d@
+}
+
+\def\sseq@d@{
+ \ifsseq@tempif
+ \csname sseq@\sseq@dtype @grabcoord\@xp\endcsname
+ \else
+ \sseq@eval{\@nx\sseq@d@main{\sseq@dtype}{\unexpanded\@xp{\sseq@d@theoptions}}{\sseq@dpage}{\sseq@NoValue}{\sseq@NoValue}}
+ \sseq@breakpoint
+ \endgroup
+ \fi
+}
+
+\sseq@DeclareDocumentCommandAs\sseq@d@grabcoord\d{d()d()}{
+ \sseq@eval{\@nx\sseq@d@main{\sseq@dtype}{\unexpanded\@xp{\sseq@d@theoptions}}{\sseq@dpage}}{#1}{#2}
+ \sseq@breakpoint
+ \endgroup
+}
+
+\sseq@DeclareDocumentCommandAs\sseq@doptions@grabcoord\doptions{d()d()}{
+ \sseq@eval{\@nx\sseq@d@main{\sseq@dtype}{\unexpanded\@xp{\sseq@d@theoptions}}{\sseq@dpage}}{#1}{#2}
+ \sseq@breakpoint
+ \endgroup
+}
+
+
+\def\sseq@d@main#1#2#3#4#5{%
+ \sseq@options@firstpassmode
+ \def\sseq@edgetype{differential}%
+ \the\sseq@sseqstyle
+ \the\sseq@edgestyle
+ \the\sseq@differentialstyle
+ \the\sseq@scope@toks
+ \sseq@options@normalmode
+ \IfNoValueF{#2}{%
+ \sseq@processoptions{differential}{#2}% Outputs result into \sseq@savedoptioncode
+ }%
+ \sseq@xsetthiscall{\@xp\string\csname#1\endcsname\IfNoValueF{#2}{\unexpanded{[#2]}}\unexpanded{#3}\IfNoValueF{#4}{\unexpanded{(#4)}}\IfNoValueF{#5}{\unexpanded{(#5)}}}%
+ \sseq@ifintexpr{#3}{%
+ \sseq@tempcount=\numexpr#3\relax % this is a convenient way to get rid of spaces because #3 has to be a number
+ }{%
+ \sseq@error@n{d-invalid-page}{#3}\sseq@break
+ }%
+ \sseq@eval{\@nx\sseq@dparsecoord{\the\sseq@tempcount}{\IfNoValueTF{#4}{\lastclass0}{\unexpanded{#4}}}{\unexpanded{#5}}}
+ \sseq@eval{\unexpanded{\csname sseq@d@#1\endcsname{#2}}{\the\sseq@tempcount}}%
+ \sseq@breakpoint
+}
+
+% #1 -- options
+% #2 -- page
+% #3 -- source coordinate
+\def\sseq@d@d#1#2{%%
+ \sseq@obj@ifdef{class.\source.hasextension}{
+ \sseq@error@xxx{d-class-has-extension}{source}{\sourcename}{#2}
+ \sseq@break
+ }{}
+ \sseq@obj@ifdef{class.\target.hasextension}{
+ \sseq@error@xxx{d-class-has-extension}{target}{\targetname}{#2}
+ \sseq@break
+ }{}
+ \sseq@d@setpageminrec{class.\source}{#2}{source}% automatically handles \sseq@cleanup@obj
+ \sseq@d@setpageminrec{class.\target}{#2}{target}
+ %%% This is the last point we might break, so now the differential is good.
+ %%% Note that we can break inside \sseq@d@setpageclass inside \sseq@d@setpageminrec
+%
+ \ifsseq@drawdifferential % TODO: probably should get rid of invisible now that we have \kill...
+ \sseq@obj@xodef{d.#2\source\target.familylist}{\sseq@familylist}
+ \sseq@obj@gdef{d.#2\source\target.page}{#2}
+ \sseq@obj@xdef{d.#2\source\target.source}{\source}
+ \sseq@obj@xdef{d.#2\source\target.target}{\target}
+ \sseq@obj@xdef{d.#2\source\target.options}{\ifsseq@needstikz\@nx\sseq@needstikztrue\fi\unexpanded\@xp{\sseq@savedoptioncode}}
+%
+ \sseq@savedpaths@adddifferential{d.#2\source\target}%
+ \else
+ \sseq@obj@gdef{d.#2\source\target.invisible}{}
+ \fi
+}
+\def\sseq@d@doptions#1#2{%
+ \sseq@obj@ifundef{d.#2\source\target.options}{\sseq@error@n{cmdoptions-feature-doesn't-exist}{differential}\sseq@break}{}%
+ \sseq@obj@ifdef{d.#2\source\target.invisible}{\sseq@error{doptions-invisible}\sseq@break}
+ \ifsseq@keepchanges\else
+ \@ifundefined{sseq@thepage}{}{
+ \ifnum#2=\sseq@thepagecount\else\ifnum\sseq@thepagecount=\z@\else
+ \sseq@error@n{doptions-no-effect-on-this-page}{#2}\sseq@breakfififi
+ \fi\fi
+ }%
+ \fi
+%
+ \sseq@cleanup@obj{d.#2\source\target.familylist}
+ \sseq@obj@xoaddto{d.#2\source\target.familylist}{\sseq@familylist}
+ \sseq@cleanup@obj{d.#2\source\target.options}
+ \sseq@obj@xaddto{d.#2\source\target.options}{\ifsseq@needstikz\@nx\sseq@needstikztrue\fi\unexpanded\@xp{\sseq@savedoptioncode}}
+}
+
+% Some helper macros:
+% #1 -- class name for \sseq@obj
+% #2 -- page
+% #3 -- source or target for error handling purposes.
+\def\sseq@d@setpageminrec#1#2#3{% not really recursive.
+ \sseq@cleanup@obj{#1[\sseq@obj{#1.num}].page}
+ \sseq@cleanup@obj{#1[\sseq@obj{#1.num}].deathinfo}
+ \sseq@cleanup@obj{#1[\sseq@obj{#1.num}].deathline}
+ \sseq@d@setpageclass{#1}{#2}{#3}
+ \sseq@obj@map{#1[\sseq@obj{#1.num}].dependencies}{
+ %\def\temp{##1}\show\temp
+ %\sseq@obj@show{#1[\sseq@obj{#1.num}].dependencies}
+ \sseq@d@setpagemin{##1}{#2} % automatically handles cleanup
+ }
+}
+
+% #1 -- class name for \sseq@obj
+% #2 -- page
+% #3 -- source or target for error handling purposes.
+\def\sseq@d@setpageclass#1#2#3{
+ \edef\sseq@classgenname{#1[\sseq@obj{#1.num}]}
+ % If the class is dead, then it's only not an error if it died on the current page
+ \ifnum\sseq@obj{\sseq@classgenname.page}<\sseq@infinitycount
+ % If the page it died on is greater than this one, hit-wrong-order error
+ \ifnum\sseq@obj{\sseq@classgenname.page}>#2\relax
+ \let\sseq@deadclass@genname\sseq@classgenname
+ \sseq@preparestacktrace
+ \sseq@error@xxxx{d-hit-wrong-order}{#3}{\csname #3name\endcsname}{#2}{\the\sseq@temptoks}
+ \sseq@breakfifi
+ \fi
+ % If the page it died on is less than this one, class-already-hit error
+ \ifnum\sseq@obj{\sseq@classgenname.page}<#2\relax
+ \let\sseq@deadclass@genname\sseq@classgenname
+ \sseq@preparestacktrace
+ \sseq@error@xxxx{d-class-already-hit}{#3}{\csname #3name\endcsname}{#2}{\the\sseq@temptoks}
+ \sseq@breakfifi
+ \fi
+ \fi
+ % If the class has been replaced before, that's only okay if it was replaced on a previous page.
+ \ifnum\sseq@obj{#1.num}>\z@
+ \sseq@tempcount=\sseq@obj{#1.num}\relax
+ \advance\sseq@tempcount\m@ne
+ \ifnum\sseq@obj{#1[\the\sseq@tempcount].page}<#2\relax\else
+ \ifnum#2=\sseq@obj{#1[\the\sseq@tempcount].page}
+ \sseq@error@xxxx{d-hit-same-page-replaceclass}{#3}{\csname #3name\endcsname}{#2}{\sseq@obj{#1[\the\sseq@tempcount].deathinfo}}
+ \sseq@breakfififi
+ \else
+ \edef\sseq@deadclass@genname{#1[\the\numexpr\sseq@obj{#1.num}-\@ne]}
+ \sseq@preparestacktrace
+ \sseq@error@xxxx{d-hit-wrong-order}{#3}{\csname #3name\endcsname}{#2}{\the\sseq@temptoks}
+ \sseq@breakfififi
+ \fi
+ \fi
+ \fi
+ \sseq@obj@xdef{\sseq@classgenname.page}{#2}
+ \sseq@obj@xdef{\sseq@classgenname.deathinfo}{#3 of differential \sseq@thiscall\ on page #2}
+ \bgroup
+ \def\foreach{\@nx\@nx\@nx\foreach}
+ \protected\def\\{}
+ \let\sseq@possibleperiod\empty
+ \let\sseq@possibleperiodb\empty
+ \sseq@obj@xdef{\sseq@classgenname.deathlocation}{%
+ on input line \sseq@inputline\sseq@error@inforeachloop
+ }
+ \sseq@obj@xdef{\sseq@classgenname.deathline}{\sseq@inputline}
+ \sseq@tempiffalse
+ \edef\sseq@temp{\the\sseq@foreachcall}
+ \ifx\sseq@temp\pgfutil@empty\else\sseq@tempiftrue\fi
+ \ifx\sseq@restofstacktrace\pgfutil@empty\else\sseq@tempiftrue\fi
+ \sseq@obj@xdef{\sseq@classgenname.deathstacktrace}{%
+ \ifsseq@tempif
+ \unexpanded{\\\\}Stack trace for differential \@nx\unexpanded{\sseq@thiscall} that killed #3:\unexpanded{\\}%
+ \ifx\sseq@temp\sseq@empty\else\unexpanded{\ \ \ }\@nx\sseq@stack@unexpanded{\unexpanded\@xp{\the\sseq@foreachcall}}\fi
+ \unexpanded\@xp{\sseq@restofstacktrace}%
+ \fi
+ }
+ \egroup
+}
+
+\def\sseq@preparestacktrace{
+ \def\foreach{\@nx\foreach}
+ \edef\sseq@temp@i{\unexpanded\@xpthree{\sseq@obj{\sseq@deadclass@genname.deathline}}}
+ \edef\sseq@temp@ii{\sseq@inputline}
+ \ifx\sseq@temp@i\sseq@temp@ii
+ \sseq@temptoks\@xpthree{\sseq@obj{\sseq@deadclass@genname.deathinfo} (in the same \foreach loop)}
+ \else
+ \protected\def\\{}
+ \sseq@eval{\sseq@temptoks{\unexpanded\@xpthree{\sseq@obj{\sseq@deadclass@genname.deathinfo}}\space(\sseq@obj{\sseq@classgenname.deathlocation})}}
+ \sseq@e@addto@macro\sseq@errorinfo{\unexpanded\@xpthree{\sseq@obj{\sseq@deadclass@genname.deathstacktrace}}}
+ \fi
+}
+
+\def\sseq@d@setpagemin#1#2{
+ \ifnum\sseq@obj{#1.page}>#2\relax
+ \ifnum\sseq@obj{#1.pagemin}>#2\relax\else % prevent a d1 from getting rid of a structline that was supposed to start on page 5
+ \sseq@cleanup@obj{#1.page}
+ \sseq@cleanup@obj{#1.page.old}
+ \sseq@obj@gletobj{#1.page.old}{#1.page}
+ \sseq@obj@xdef{#1.page}{#2}
+ \fi
+ \fi
+}
+\def\sseq@differential@draw@ifpage#1{
+ \sseq@drawtrue
+ \sseq@obj{#1.familylist}
+ \ifsseq@draw
+ \ifnum\sseq@thepagecount=\z@
+ \sseq@drawdifferential{#1}
+ \else
+ \ifnum\sseq@obj{#1.page}>\sseq@thepagemm\relax
+ \ifnum\sseq@obj{#1.page}<\sseq@thepagemaxpp\relax
+ \sseq@drawdifferential{#1}
+ \fi
+ \fi
+ \fi
+ \fi
+}
+\def\sseq@drawdifferential#1{
+ \bgroup
+ \sseq@eval{
+ \@nx\sseq@drawedge\sseq@obj{#1.source}\sseq@obj{#1.target}{differential}{%
+ \@nx\sseq@obj{#1.options}
+ }
+ }
+ \egroup
+}
+%%%
+%%% \structline and \structlineoptions
+%%%
+
+\sseq@DeclareDocumentCommand\structline{od()}{%
+ \begingroup
+ \sseq@loadinputline
+ \IfNoValueTF{#2}{
+ \sseq@structline@main{structline}{#1}{#2}{#2}
+ }{
+ \def\next{\sseq@structline@@{structline}{#1}{#2}}%
+ \@xptwo\next\sseq@trimleadingspaces
+ }
+}
+\sseq@DeclareDocumentCommand\structlineoptions{od()}{%
+ \begingroup
+ \sseq@loadinputline
+ \IfNoValueTF{#2}{
+ \sseq@structline@main{structlineoptions}{#1}{#2}{#2}
+ }{
+ \def\next{\sseq@structlineoptions@@{structlineoptions}{#1}{#2}}%
+ \@xptwo\next\sseq@trimleadingspaces
+ }
+}
+
+\sseq@DeclareDocumentCommandAs\sseq@structline@@\structline{mmmd()}{%
+ \sseq@structline@main{#1}{#2}{#3}{#4}
+}
+
+\sseq@DeclareDocumentCommandAs\sseq@structlineoptions@@\structlineoptions{mmmd()}{%
+ \sseq@structline@main{#1}{#2}{#3}{#4}
+}
+
+\def\sseq@structline@main#1#2#3#4{
+ \sseq@xsetthiscall{\string\structline\IfNoValueF{#2}{\unexpanded{[#2]}}\IfNoValueF{#3}{\unexpanded{(#3)}\IfNoValueF{#4}{\unexpanded{(#4)}}}}%
+ \def\sseq@edgetype{structline}
+ \sseq@options@firstpassmode
+ \the\sseq@sseqstyle
+ \the\sseq@edgestyle
+ \the\sseq@structlinestyle
+ \the\sseq@scope@toks
+ \sseq@options@normalmode
+ \IfNoValueF{#2}{%
+ \sseq@processoptions{struct line}{#2}% Outputs result into \sseq@savedoptioncode
+ }%
+ \def\sourcename{#3}%
+ \def\targetname{#4}%
+ \IfNoValueTF{#3}{%
+ \def\sourcename{\lastclass1}%
+ \def\targetname{\lastclass}%
+ }{%
+ \IfNoValueT{#4}{%
+ \def\targetname{\lastclass}%
+ }%
+ }%
+ \sseq@parsecoord\source{\sourcename}{source of structure line}%
+ \sseq@parsecoord\target{\targetname}{target of structure line}%
+ % Sort \xsource and \xtarget. This is to ensure that if a person adds
+ \ifnum\xsource>\xtarget\relax
+ \sseq@parsedcoords@swap\source\target
+ \else
+ \ifnum\xsource=\xtarget\relax
+ \ifnum\ysource>\ytarget\relax
+ \sseq@parsedcoords@swap\source\target
+ \else
+ \ifnum\ysource=\ytarget\relax
+ \ifnum\nsource>\ntarget\relax
+ \sseq@parsedcoords@swap\source\target
+ \else
+ \ifnum\nsource=\ntarget\relax
+ \sseq@error@n{edge-source-target-equal}{\structline}%
+ \sseq@fbreak
+ \fi
+ \fi
+ \fi
+ \fi
+ \fi
+ \fi
+ \relax % stop fbreak
+ \@ifundefined{sseq@structline@page}{%this is repeated in circleclasses
+ \def\sseq@pagemin{0}
+ \ifnum\sseq@obj{class.\source.num}>\z@
+ \edef\sseq@pagemin{\sseq@obj{class.\source[\the\numexpr\sseq@obj{class.\source.num}-1].page}}
+ \fi
+ \ifnum\sseq@obj{class.\target.num}>\z@
+ \edef\sseq@test{\sseq@obj{class.\target[\the\numexpr\sseq@obj{class.\target.num}-1].page}}
+ \ifnum\sseq@test>\sseq@pagemin\relax
+ \let\sseq@pagemin\sseq@test
+ \fi
+ \fi
+ }{\edef\sseq@pagemin{\the\numexpr\sseq@structline@page-\@ne}}
+ \csname sseq@structline@#1\endcsname
+ \sseq@breakpoint
+ \endgroup
+}
+\def\sseq@structline@structline{
+ \@ifundefined{sseq@structline@pagemax}{}{\ifnum\sseq@structline@pagemax=\sseq@infinity\else \sseq@error{structline-no-page-max}\fi}% could be warning
+ \ifnum\sseq@pagemin<\sseq@obj{class.\source[\sseq@obj{class.\source.num}].page}\relax\else
+ \sseq@error{structline-stillborn}
+ \sseq@breakfi
+ \fi
+ \ifnum\sseq@pagemin<\sseq@obj{class.\target[\sseq@obj{class.\target.num}].page}\relax\else % If the \structline is dead before it is born, throw an error
+ \sseq@error{structline-stillborn}
+ \sseq@breakfi
+ \fi
+ \sseq@obj@ifdef{structline.\source\target.num}{%
+ \ifnum\sseq@obj{structline.\source\target[\sseq@obj{structline.\source\target.num}].page}=\sseq@infinitycount
+ \sseq@error@xx{structline-already-exists}{\sourcename}{\targetname}
+ \sseq@breakfi
+ \fi
+ \sseq@cleanup@obj{structline.\source\target.num}
+ \sseq@obj@inc{structline.\source\target.num}
+ }{
+ \sseq@cleanup@obj{structline.\source\target.num}
+ \sseq@obj@gdef{structline.\source\target.num}{0}
+ \sseq@savedpaths@addstructline{structline.\source\target}
+ }
+ \edef\sseq@structlinename{structline.\source\target[\sseq@obj{structline.\source\target.num}]}
+ % The following cleanup call is needed to prevent a glitch where if a \structline is made inside of a temporary sseqpage environment, then later attempts
+ % to put a structline throw a "structline already exists" error
+ \sseq@cleanup@obj{\sseq@structlinename.page}
+ \sseq@obj@xdef{\sseq@structlinename.page}{\sseq@infinity}
+ \sseq@obj@xdef{\sseq@structlinename.pagemin}{\sseq@pagemin}
+ \sseq@obj@xdef{\sseq@structlinename.source}{\source}
+ \sseq@obj@xdef{\sseq@structlinename.target}{\target}
+ \sseq@class@adddependence{class.\source}{\sseq@structlinename}% If the source or target is already dead, this will set structlinename.page
+ \sseq@class@adddependence{class.\target}{\sseq@structlinename}
+ \sseq@obj@xodef{\sseq@structlinename.familylist}{\sseq@familylist}
+ \sseq@cleanup@obj{\sseq@structlinename.options}
+ \sseq@obj@xdef{\sseq@structlinename.options}{\ifsseq@needstikz\@nx\sseq@needstikztrue\fi\unexpanded\@xp{\sseq@savedoptioncode}}
+}
+\def\sseq@structline@structlineoptions{
+ \sseq@obj@ifundef{structline.\source\target.num}{
+ \sseq@error@n{cmdoptions-feature-doesn't-exist}{\structline}
+ \sseq@break
+ }{}
+ \@ifundefined{sseq@structline@page}{
+ \edef\sseq@gen{\sseq@obj{structline.\source\target.num}}
+ \let\sseq@genmax\sseq@gen
+ }{
+ \sseq@structline@getgen{\sseq@structline@page}
+ \@ifundefined{sseq@structline@pagemax}{%
+ % If no max is present, apply options to all pages starting at specified minimum
+ \edef\sseq@genmax{\sseq@obj{structline.\source\target.num}}%
+ \let\sseq@structline@pagemax\sseq@infinity
+ }{
+ \let\sseq@genmin\sseq@gen
+ \sseq@structline@getgen{\sseq@structline@pagemax}
+ \let\sseq@genmax\sseq@gen
+ \let\sseq@gen\sseq@genmin
+ }
+ \edef\sseq@structline@page{\the\numexpr\sseq@structline@page-1}
+ \edef\sseq@structline@pagemax{\the\numexpr\sseq@structline@pagemax+1}
+ }
+ \sseq@tempcount=\sseq@gen\relax
+ \advance\sseq@tempcount\m@ne
+ \loop
+ \advance\sseq@tempcount\@ne
+ \edef\sseq@structlinename{structline.\source\target[\the\sseq@tempcount]}
+ \sseq@cleanup@obj{\sseq@structlinename.options}
+ \sseq@cleanup@obj{\sseq@structlinename.familylist}
+ \ifsseq@needstikz
+ \sseq@obj@gaddto{\sseq@structlinename.options}{\sseq@needstikztrue}
+ \fi
+ \ifnum\sseq@tempcount=\sseq@gen\relax
+ \@ifundefined{sseq@structline@page}{}{
+ \edef\sseq@savedoptioncode{\@nx\ifnum\sseq@thepagecount>\sseq@structline@page\relax\unexpanded\@xp{\sseq@savedoptioncode}\@nx\fi}
+ \edef\sseq@familylist{\@nx\ifnum\sseq@thepagecount>\sseq@structline@page\relax\unexpanded\@xp{\sseq@familylist}\@nx\fi}
+ }
+ \fi
+ \ifnum\sseq@tempcount=\sseq@genmax\relax
+ \@ifundefined{sseq@structline@pagemax}{}{
+ \edef\sseq@savedoptioncode{\@nx\ifnum\sseq@thepagecount<\sseq@structline@pagemax\relax\unexpanded\@xp{\sseq@savedoptioncode}\@nx\fi}
+ \edef\sseq@familylist{\@nx\ifnum\sseq@thepagecount<\sseq@structline@pagemax\relax\unexpanded\@xp{\sseq@familylist}\@nx\fi}
+ }
+ \fi
+ \sseq@obj@xoaddto{\sseq@structlinename.options}{\sseq@savedoptioncode}
+ \sseq@obj@xoaddto{\sseq@structlinename.familylist}{\sseq@familylist}
+ \ifnum\sseq@tempcount<\sseq@genmax\repeat
+}
+
+\def\sseq@structline@copy#1#2{
+ \begingroup
+ \sseq@structline@copy@decomposename#2
+ \let\sseq@thename@saved\sseq@thename
+ \def\sseq@pagemin{0}
+ \sseq@parsecoord\source{\sourcename}{source of structure line}%
+ \sseq@parsecoord\target{\targetname}{target of structure line}
+ \def\sseq@thename{#1}
+ \sseq@lettoobj\sseq@familylist{#2.familylist}
+ \sseq@lettoobj\sseq@savedoptioncode{#2.options}
+ \let\sseq@thename\sseq@thename@saved
+ \sseq@structline@structline
+ \endgroup
+}
+
+\def\sseq@structline@copy@decomposename#1.(#2,#3,#4)(#5,#6,#7){
+ \def\sourcename{#2,#3,copiedfrom#4}
+ \def\targetname{#5,#6,copiedfrom#7}
+}
+
+\def\sseq@structline@getgen#1{
+ \bgroup
+ \sseq@tempcount=\sseq@obj{structline.\source\target.num}\relax
+ \ifnum\sseq@tempcount>\z@
+ \ifnum#1<\z@\relax
+ \@ifundefined{sseq@thepage}{
+ \ifnum\sseq@obj{structline.\source\target[\the\sseq@tempcount].page}<\sseq@infinitycount
+ \sseq@tempcount=\sseq@obj{structline.\source\target[\the\sseq@tempcount].page}\relax
+ \else
+ \advance\sseq@tempcount\m@ne
+ \sseq@tempcount=\sseq@obj{structline.\source\target[\the\sseq@tempcount].page}\relax
+ \advance\sseq@tempcount\@ne
+ \fi
+ }{
+ \sseq@tempcount=\sseq@thepagecount
+ }
+ \advance\sseq@tempcount\@ne
+ \advance\sseq@tempcount#1\relax
+ \fi
+ \sseq@eval{\unexpanded{\sseq@obj@pagetogen{structline.\source\target}}{\the\sseq@tempcount}} % stores in \sseq@gen
+ \else
+ \def\sseq@gen{0}
+ \fi
+ \sseq@smuggle@macro\sseq@gen
+ \egroup
+}
+\def\sseq@class@adddependence#1#2{
+ \edef\sseq@temp{\sseq@obj{#1.num}}
+ \sseq@d@setpagemin{#2}{\sseq@obj{#1[\sseq@temp].page}}% automatically handles cleanup
+ \sseq@cleanup@obj{#1[\sseq@temp].dependencies}
+ \sseq@obj@xaddto{#1[\sseq@temp].dependencies}{\@nx\\{#2}}
+}
+\def\sseq@structline@draw@ifpage#1{
+ \bgroup
+ \ifnum\sseq@thepagecount=\z@
+ \ifnum\sseq@obj{#1[0].pagemin}=\z@
+ \sseq@drawtrue
+ \sseq@obj{#1[0].familylist}
+ \ifsseq@draw
+ \sseq@structline@draw{#1[0]}
+ \fi
+ \fi
+ \else
+ \sseq@obj@pagetogen{#1}{\sseq@thepage} % sets \sseq@gen
+ \ifnum\sseq@gen<\z@\else % negative sseq@gen means don't draw it
+ \ifnum\sseq@obj{#1[\sseq@gen].page}=\sseq@thepagecount
+ \sseq@thispagetrue
+ \fi
+ \ifnum\sseq@obj{#1[\sseq@gen].pagemin}<\sseq@thepagecount
+ \sseq@drawtrue
+ \sseq@obj{#1[\sseq@gen].familylist}
+ \ifsseq@draw
+ \sseq@structline@draw{#1[\sseq@gen]}
+ \fi
+ \fi
+ \fi
+ \fi
+ \egroup
+}
+\def\sseq@structline@draw#1{\sseq@eval{\@nx\sseq@drawedge\sseq@obj{#1.source}\sseq@obj{#1.target}{structline}{\@nx\sseq@obj{#1.options}}}}
+
+
+
+\sseq@DeclareDocumentCommand\extension{od()}{%
+ \begingroup
+ \sseq@loadinputline
+ \IfNoValueTF{#2}{
+ \sseq@extension@main{extension}{#1}{#2}{#2}
+ }{
+ \def\next{\sseq@extension@@{extension}{#1}{#2}}%
+ \@xptwo\next\sseq@trimleadingspaces
+ }
+}
+\sseq@DeclareDocumentCommand\extensionoptions{od()}{%
+ \begingroup
+ \sseq@loadinputline
+ \IfNoValueTF{#2}{
+ \sseq@structline@main{extensionoptions}{#1}{#2}{#2}
+ }{
+ \def\next{\sseq@extensionoptions@@{extensionoptions}{#1}{#2}}%
+ \@xptwo\next\sseq@trimleadingspaces
+ }
+}
+
+\sseq@DeclareDocumentCommandAs\sseq@extension@@\extension{mmmd()}{%
+ \sseq@extension@main{#1}{#2}{#3}{#4}
+}
+
+\sseq@DeclareDocumentCommandAs\sseq@extensionoptions@@\extensionoptions{mmmd()}{%
+ \sseq@extension@main{#1}{#2}{#3}{#4}
+}
+
+\def\sseq@extension@main#1#2#3#4{
+ \sseq@xsetthiscall{\string\extension\IfNoValueF{#2}{\unexpanded{[#2]}}\IfNoValueF{#3}{\unexpanded{(#3)}\IfNoValueF{#4}{\unexpanded{(#4)}}}}%
+ \def\sseq@edgetype{structline}
+ \sseq@options@firstpassmode
+ \the\sseq@sseqstyle
+ \the\sseq@edgestyle
+ \the\sseq@structlinestyle
+ \the\sseq@scope@toks
+ \sseq@options@normalmode
+ \IfNoValueF{#2}{%
+ \sseq@processoptions{extension}{#2}% Outputs result into \sseq@savedoptioncode
+ }%
+ \def\sourcename{#3}%
+ \def\targetname{#4}%
+ \IfNoValueTF{#3}{%
+ \def\sourcename{\lastclass1}%
+ \def\targetname{\lastclass}%
+ }{%
+ \IfNoValueT{#4}{%
+ \def\targetname{\lastclass}%
+ }%
+ }%
+ \sseq@parsecoord\source{\sourcename}{source of extension}%
+ \sseq@parsecoord\target{\targetname}{target of extension}%
+ \ifnum\xsource>\xtarget\relax
+ \sseq@parsedcoords@swap\source\target
+ \else
+ \ifnum\xsource=\xtarget\relax
+ \ifnum\ysource>\ytarget\relax
+ \sseq@parsedcoords@swap\source\target
+ \else
+ \ifnum\ysource=\ytarget\relax
+ \ifnum\nsource>\ntarget\relax
+ \sseq@parsedcoords@swap\source\target
+ \else
+ \ifnum\nsource=\ntarget\relax
+ \sseq@error@n{edge-source-target-equal}{\extension}%
+ \sseq@fbreak
+ \fi
+ \fi
+ \fi
+ \fi
+ \fi
+ \fi
+ \relax % stop fbreak
+ \csname sseq@extension@#1\endcsname
+ \sseq@breakpoint
+ \endgroup
+}
+\def\sseq@extension@extension{
+ \ifnum\sseq@obj{class.\source[\sseq@obj{class.\source.num}].page}<\sseq@infinity\relax
+ \sseq@error{extension-stillborn}
+ \sseq@breakfi
+ \fi
+ \ifnum\sseq@obj{class.\target[\sseq@obj{class.\target.num}].page}<\sseq@infinity\relax
+ \sseq@error{extension-stillborn}
+ \sseq@breakfi
+ \fi
+ \edef\sseq@extensionname{extension.\source\target}
+ \sseq@obj@ifdef{\sseq@extensionname.source}{%
+ \sseq@error@xx{extension-already-exists}{\sourcename}{\targetname}
+ \sseq@breakfi
+ }{
+ \sseq@savedpaths@addextension{extension.\source\target}
+ }
+ \sseq@cleanup@obj{class.\source.hasextension}
+ \sseq@obj@gdef{class.\source.hasextension}{}
+ \sseq@cleanup@obj{class.\target.hasextension}
+ \sseq@obj@gdef{class.\target.hasextension}{}
+ \sseq@cleanup@obj{\sseq@extensionname.source}
+ \sseq@obj@xdef{\sseq@extensionname.source}{\source}
+ \sseq@obj@xdef{\sseq@extensionname.target}{\target}
+ \sseq@obj@xodef{\sseq@extensionname.familylist}{\sseq@familylist}
+ \sseq@cleanup@obj{\sseq@extensionname.options}
+ \sseq@obj@xdef{\sseq@extensionname.options}{\ifsseq@needstikz\@nx\sseq@needstikztrue\fi\unexpanded\@xp{\sseq@savedoptioncode}}
+}
+\def\sseq@extension@extensionoptions{
+ \edef\sseq@extensionname{extension.\source\target}
+ \sseq@obj@ifundef{\sseq@extensionname.source}{
+ \sseq@error@n{cmdoptions-feature-doesn't-exist}{\extension}
+ \sseq@break
+ }{}
+ \sseq@cleanup@obj{\sseq@extensionname.options}
+ \sseq@cleanup@obj{\sseq@extensionname.familylist}
+ \ifsseq@needstikz
+ \sseq@obj@gaddto{\sseq@extensionname.options}{\sseq@needstikztrue}
+ \fi
+ \sseq@obj@xoaddto{\sseq@extensionname.options}{\sseq@savedoptioncode}
+ \sseq@obj@xoaddto{\sseq@extensionname.familylist}{\sseq@familylist}
+}
+
+\def\sseq@extension@copy#1#2{
+ \begingroup
+ \sseq@structline@copy@decomposename#2
+ \let\sseq@thename@saved\sseq@thename
+ \def\sseq@pagemin{0}
+ \sseq@parsecoord\source{\sourcename}{source of structure line}%
+ \sseq@parsecoord\target{\targetname}{target of structure line}
+ \def\sseq@thename{#1}
+ \sseq@lettoobj\sseq@familylist{#2.familylist}
+ \sseq@lettoobj\sseq@savedoptioncode{#2.options}
+ \let\sseq@thename\sseq@thename@saved
+ \sseq@extension@extension
+ \endgroup
+}
+
+
+\def\sseq@extension@draw@ifpage#1{
+ \bgroup
+ \ifnum\sseq@thepage=\sseq@infinity\relax
+ \sseq@extension@draw{#1}
+ \fi
+ \egroup
+}
+\def\sseq@extension@draw#1{\sseq@eval{\@nx\sseq@drawedge\sseq@obj{#1.source}\sseq@obj{#1.target}{structline}{\@nx\sseq@obj{#1.options}}}}
+
+%% This is a dumb place to put this. It has to be after the differential commands are defined.
+\ifsseq@draftmode % Okay, have to dummy out all the main commands.
+ % Only add tikz background paths to the savedpaths. We need to draw background paths because they might change the bounding box!
+ \sseq@setsavedpaths@draftmode
+ \let\sseq@handlequote\@gobble
+%
+ % All we want to do here is record the range. Have to process options in order to figure out where it is.
+ \sseq@DeclareDocumentCommand\class{or()}{%
+ \begingroup
+ \sseq@loadinputline
+ \IfNoValueT{#2}{\sseq@break}%
+ \sseq@xsetthiscall{\string\class\IfNoValueF{#1}{[\unexpanded{#1}]}\unexpanded{(#2)}}%
+ \sseq@options@firstpassmode
+ \the\sseq@sseqstyle
+ \the\sseq@classstyle
+ \sseq@options@normalmode
+ \IfNoValueF{#1}{%
+ \sseq@processoptions{class}{#1}% Outputs result into \sseq@savedoptioncode
+ }%
+ \sseq@cparsecoord{#2}{}%
+ \sseq@xminmax{\xcoord}{\ycoord}%
+ \sseq@yminmax{\xcoord}{\ycoord}%
+ \sseq@pushstack@\coord % also need the class stack to calculate other \class commands.
+ \sseq@cleanup@obj{partcoord.(\partialcoord).numnodes}%
+ \ifnum\nodenum=\@ne
+ \sseq@obj@gdef{partcoord.(\partialcoord).numnodes}{0}%
+ \fi
+ \sseq@obj@xdef{partcoord.(\partialcoord).numnodes}{\nodenum}%
+ \edef\classname{class.\coord[0]}%
+ \ifx\sseq@class@tag\pgfutil@empty\else
+ \sseq@obj@ifdef{partcoord.(\partialcoord).tag.\sseq@class@tagprefix\sseq@class@tag}{%
+ \sseq@error@xx{class-tag-already-defined}{\sseq@class@tagprefix\sseq@class@tag}{(\partialcoord)}% could be warning
+ }{}%
+ \sseq@cleanup@obj{partcoord.(\partialcoord).tag.\sseq@class@tagprefix\sseq@class@tag}%
+ \sseq@obj@xdef{partcoord.(\partialcoord).tag.\sseq@class@tagprefix\sseq@class@tag}{\nodenum}%
+ \fi
+ \ifx\sseq@class@name\pgfutil@empty\else
+ \sseq@cleanup@obj{\classname.name}
+ \sseq@cleanup@obj{class.namedclass.\detokenize\@xp{\sseq@class@name}}
+ \sseq@namehandler
+ \sseq@obj@xdef{\classname.name}{\sseq@class@name}
+ \sseq@obj@xdef{class.namedclass.\detokenize\@xp{\sseq@class@name}}{\coordnopar}
+ \fi
+ \sseq@breakpoint
+ \endgroup
+ }
+
+ % Now the rest of these can be no-ops
+ \sseq@DeclareDocumentCommand\classoptions{od()}{}
+ \sseq@DeclareDocumentCommand\replaceclass{od()}{}
+ \sseq@DeclareDocumentCommand\replacesource{o}{}
+ \sseq@DeclareDocumentCommand\replacetarget{o}{}
+ \def\sseq@d@main#1#2#3#4#5{}
+ \DeclareDocumentCommand \sseq@DrawIfValidDifferential@ { mmO{} } {
+ \sseq@loadinputline
+ \def\sseq@dtype{d}
+ \def\sseq@trueclause{#1}
+ \def\sseq@falseclause{#2}
+ \sseq@d@grabpage\sseq@DrawIfValidDifferential@@
+ }
+ \DeclareDocumentCommand \sseq@DrawIfValidDifferential@@ { d() d() } {
+ \csname sseq@DrawIfValidDifferential@@handle@truefalse%
+ @@\sseq@trueclause\sseq@falseclause
+ \endcsname
+ }
+ % Have to do something in case it branches. We somewhat arbitrarily always
+ % pretend it branched yes.
+ \def\sseq@DrawIfValidDifferential@@handle@truefalse@@{}
+ \def\sseq@DrawIfValidDifferential@@handle@truefalse@@T{\@firstofone}
+ \def\sseq@DrawIfValidDifferential@@handle@truefalse@@F{\@gobble}
+ \def\sseq@DrawIfValidDifferential@@handle@truefalse@@TF{\@firstoftwo}
+
+ \sseq@DeclareDocumentCommand\structline{od()d()}{}
+ \sseq@DeclareDocumentCommand\structlineoptions{od()d()}{}
+ \sseq@DeclareDocumentCommand\circleclasses{or()r()}{}
+ \sseq@DeclareDocumentCommand\gettag{mr()}{}
+ \expandafter\endinput
+\fi
+
+
+\newcount\sseq@circleclassuid
+\sseq@DeclareDocumentCommand\circleclasses{or()r()}{
+ \begingroup
+ \sseq@loadinputline
+ \def\sseq@keysfortikzprim{}
+ \IfNoValueF{#1}{
+ \sseq@processoptions{fit}{#1}
+ }
+ \sseq@parsecoord\coorda{#2}{first \@nx\circleclass class}
+ \sseq@parsecoord\coordb{#3}{second \@nx\circleclass class}
+%
+%
+ \ifsseq@keepchanges
+ \@ifundefined{sseq@fit@page}{% also in structline@main. TODO: refactor these chunks together?
+ \def\sseq@pagemin{0}
+ \ifnum\sseq@obj{class.\coorda.num}>\z@
+ \edef\sseq@pagemin{\sseq@obj{class.\coorda[\the\numexpr\sseq@obj{class.\coorda.num}-\@ne].page}}
+ \fi
+ \ifnum\sseq@obj{class.\coordb.num}>\z@
+ \edef\sseq@test{\sseq@obj{class.\coordb[\the\numexpr\sseq@obj{class.\coordb.num}-\@ne].page}}
+ \ifnum\sseq@test>\sseq@pagemin\relax
+ \let\sseq@pagemin\sseq@test
+ \fi
+ \fi
+ }{\edef\sseq@pagemin{\the\numexpr\sseq@fit@page-\@ne}}
+ \@ifundefined{sseq@fit@pagemax}{\let\sseq@pagemax\sseq@infinity}{\let\sseq@pagemax\sseq@fit@pagemax}
+ \else % Put it on the current page no matter what inside sseqpage
+ \let\sseq@pagemax\sseq@infinity\def\sseq@pagemin{-1}
+ \fi
+%
+%
+ \global\advance\sseq@circleclassuid\@ne
+ \edef\circleclassobjname{circleclass(\coordanopar)(\coordbnopar)\the\sseq@circleclassuid}
+%
+%
+ \sseq@obj@xodef{\circleclassobjname.options}{\sseq@savedoptioncode}
+ \sseq@obj@xodef{\circleclassobjname.familylist}{\sseq@familylist}
+ \sseq@obj@xodef{\circleclassobjname.tikzprimoptions}{\sseq@keysfortikzprim}
+ \sseq@obj@xdef{\circleclassobjname.fitnodes}{\@nx\tikz@lib@fit{(sseq{\coordanopar})(sseq{\coordbnopar})\sseq@fitalso}}
+ %\edef\temp{\@nx\sseq@circleclass@draw{\coorda@internalname}{\coordb@internalname}{\unexpanded\@xp{\sseq@savedoptioncode}}}
+ \sseq@savedpaths@xadd{\@nx\sseq@circleclass@object{\circleclassobjname}{\coordanopar}{\coordbnopar}}
+ \sseq@obj@xdef{\circleclassobjname.page}{\sseq@pagemax}
+ \sseq@obj@xdef{\circleclassobjname.pagemin}{\sseq@pagemin}
+ \@ifundefined{sseq@fit@pagemax}{
+ \ifsseq@keepchanges
+ \sseq@class@adddependence{class.\coorda}{\circleclassobjname}% If the source or target is already dead, this will set structlinename.page
+ \sseq@class@adddependence{class.\coordb}{\circleclassobjname}
+ \fi
+ }{}
+ \sseq@breakpoint
+ \endgroup
+}
+
+\ifsseq@patchfit\else
+ \sseq@DeclareDocumentCommand\circleclasses{or()r()}{%
+ \sseq@warning{circleclasses-not-provided}
+ }
+\fi
+
+\def\sseq@circleclass@draw@ifpage#1#2#3{
+ \sseq@drawtrue
+ \sseq@obj{#1.familylist}
+ \ifsseq@draw
+ \ifnum\sseq@thepagecount<\numexpr\sseq@obj{#1.page}+\@ne
+ \ifnum\sseq@obj{#1.page}=\sseq@thepagecount
+ \sseq@thispagetrue
+ \fi
+ \ifnum\sseq@obj{#1.pagemin}<\sseq@thepagecount
+ \sseq@circleclass@draw{#1}{#2}{#3}
+ \fi
+ \fi
+ \fi
+}
+
+%%
+%% Labels
+%%
+%% This code is called from the key value handler, but it has more to do with the code in this file so I am putting it here. Search in sseqkeys.code.tex for "Labels"
+%%
+
+\def\sseq@processlabels@default{%
+ \global\let\sseq@classnodetext\pgfutil@empty
+ \global\let\sseq@classnodetextoptions\pgfutil@empty
+ \global\let\sseq@classlabelnodes\pgfutil@empty
+}
+\let\sseq@processlabels\sseq@processlabels@default
+
+\let\sseqlastlabel\empty
+% I can't remember why we need to defer this. If at some point I figure this out, I should explain why here and in the definition of \class...
+\def\sseq@handleclassquotes@inner#1#2{%
+ \begingroup\pgfscope
+ \def\sseq@classquotes@smugglehook{}
+ \def\sseq@classquotes@smugglehook@inner{}
+ \sseq@options@firstpassmode
+ \sseq@thesseqstyle
+ \sseq@thelabelstyle
+ \sseq@theclasslabelstyle
+ \the\sseq@scope@toks
+ \sseq@options@normalmode
+ \gdef\sseqlastlabel{#2}
+ \sseq@processoptions{class/label}{#2}%
+ \sseq@classlabel@handler{#1}% labeltextfn is a private handler for transformations, sseq@classlabel@handler is exposed via "class label handler"
+ \ifsseq@classlabel
+ \sseq@options@firstpassmode
+ \the\sseq@outerclasslabelstyle
+ \sseq@x@addto@macro\sseq@classlabelnodes{%
+ \@nx\sseq@drawlabel{%
+ \sseq@maybemathswitch\unexpanded\@xp{\result}\sseq@maybemathswitch}%
+ {% This is set up to prevent a bug where \classoptions and class label style don't work correctly together
+ \the\sseq@sseqstyle\@nx\the\sseq@sseqstyle@page
+ \the\sseq@labelstyle\@nx\the\sseq@labelstyle@page
+ \the\sseq@classlabelstyle\@nx\the\sseq@classlabelstyle@page
+ \sseq@theouterclasslabelstyle
+ \@nx\sseq@collections@labels@hook
+ \@nx\sseq@collections@classlabels@hook
+ \@nx\sseq@collections@outerclasslabels@hook
+ \unexpanded\@xp{\sseq@savedoptioncode}%
+ }%
+ }%
+ \else
+ \let\sseq@classquotes@smugglehook\sseq@classquotes@smugglehook@inner
+ \sseq@options@firstpassmode
+ \the\sseq@innerclasslabelstyle
+ \sseq@options@normalmode
+ \sseq@protectedeval{\@nx\pgfqkeys{/sseqpages/class/label}{#2}}%
+ \sseq@protected@xdef\sseq@classnodetext{\sseq@maybemathswitch\result\sseq@maybemathswitch}%
+ \xdef\sseq@classnodetextoptions{%
+ %\unexpanded{\def\pgfkeysdefaultpath{/sseqpages/class/node texts/}}%
+ \sseq@thelabelstyle\sseq@theclasslabelstyle\sseq@theinnerclasslabelstyle
+ \@nx\sseq@collections@labels@hook
+ \@nx\sseq@collections@classlabels@hook
+ \@nx\sseq@collections@innerclasslabels@hook
+ \unexpanded\@xp{\sseq@savedoptioncode}%
+ }%
+ \fi
+ \@xp\endpgfscope\@xp\endgroup
+ \sseq@classquotes@smugglehook
+}
+
+\def\sseq@handleedgequotes#1#2{% We use this via \let\tikz@quotes@as\sseq@handleedgequotes
+ /utils/exec={% so it's called inside \pgfkeysalso{stuff}. Need to surround with /utils/exec={} to get out.
+ \let\sseq@savedoptioncode@save\sseq@savedoptioncode
+ \sseq@processoptions{label}{#2}
+ \sseq@needstikztrue
+ \let\sseq@temp\sseq@savedoptioncode
+ \let\sseq@savedoptioncode\sseq@savedoptioncode@save
+ \sseq@e@addto@options{
+ \@nx\tikzset{
+ every to/.append style={
+ edge node={
+ node [/sseqpages/label,
+ /utils/exec={
+ \let\@nx\sseq@tikz@transform@save\@nx\tikz@transform
+ \let\@nx\tikz@transform\relax
+ \@nx\sseq@thelabelstyle
+ \@nx\sseq@collections@labels@hook
+ \@nx\sseq@theedgelabelstyle
+ \@nx\sseq@collections@edgelabels@hook
+ \@xp\@nx\csname sseq@the\sseq@edgetype labelstyle\endcsname
+ \@xp\@nx\csname sseq@collections@\sseq@edgetype labels@hook\endcsname
+ \unexpanded\@xp{\sseq@temp}
+ \ifsseq@rotatelabels\iftikz@fullytransformed\else\@nx\tikz@fullytransformedtrue\@nx\tikz@addtransform{\@nx\pgftransformresetnontranslations\@nx\sseq@globalrotatetransform}\fi\fi
+ \unexpanded\@xp{\sseq@edgeoptionhook} % this is just to make description work
+ \let\@nx\tikz@transform\@nx\sseq@tikz@transform@save
+ }
+ ]{\sseq@labeltextfn{#1}}
+ }
+ }
+ }
+ }
+ }
+}
+
+
diff --git a/graphics/pgf/contrib/spectralsequences/src/sseqmessages.code.tex b/graphics/pgf/contrib/spectralsequences/src/sseqmessages.code.tex
new file mode 100644
index 0000000000..fc735f3014
--- /dev/null
+++ b/graphics/pgf/contrib/spectralsequences/src/sseqmessages.code.tex
@@ -0,0 +1,585 @@
+%%
+%% Package: spectralsequences v1.3.0 2021-07-18
+%% Author: Hood Chatham
+%% Email: hood@mit.edu
+%% Date: 2021-07-18
+%% License: Latex Project Public License
+%%
+%% File: sseqmessages.code.tex
+%% Exposes: Error message redirects?
+%%
+%% Set up fancy error messages
+%%
+
+
+
+%% The error handling system is build on top of l3msg. l3msg produces errors like:
+%%
+%% !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+%% !
+%% ! LaTeX error: "<package>/<error-name>"
+%% !
+%% ! <error message text>
+%% !
+%% ! See the <package> documentation for further information. % produced by \msg_see_documentation_text:n{<package>}
+%% !...............................................
+%%
+%% The user will see underneath it l.<line number> <text on line>
+%%
+%% Unfortunately, if the error is in a \foreach statement, the user will only see the final line of the foreach loop, often just the close brace.
+%% It's impossible to tell from this where in the \foreach the error occurred. Similar problems occur if the command is inside a user command.
+%%
+%% We're going to modify this file to improve the situation.
+%%
+%% If the command is in a forloop or a user command I want to:
+%% -- annotate the error with the command that caused it, or the previous command if the error occurred before I can know what the current command looks like.
+%% -- present a stack trace to inform the user where exactly the problem occurred
+%%
+%%
+%% We automatically print the annotation and stack trace at the end of all l3msg messages by hooking into \msg_see_documentation_text:n.
+%% The annotation and stack trace are prepared by a command \sseq@error@setup which we hook into the beginning of the latex3 message processing code.
+%%
+%% Here's an example of the result:
+%%
+%% !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+%% !
+%% ! spectralsequences error: "index-too-large"
+%% !
+%% ! Index '-3' provided for target of structure line is too large -- only 1
+%% ! classes exist at position (32,8).
+%% !
+%% ! (Error occurred during call "\structline(16,-1,-1)(16,0,-3)", in the % This is the annotation
+%% ! \foreach block that ends on line 158, with variable values: \n = 2.) % it's stored in \sseq@error@annotation
+%% !
+%% ! Stack trace: % This is the stack trace
+%% ! \towergroupb (8*\n ,4*\n ) % Produced by \sseq@printstacktrace
+%% ! \foreach \n in {2,3,4}
+%% !
+%% ! See the spectralsequences documentation for further information.
+%% !
+%% ! Type <return> to continue.
+%% !...............................................
+
+
+\def\sseq@protecterror#1{\@nx\sseq@protecterror\unexpanded{{#1}}}
+
+% Stack, thiscall, foreachcall.
+% Here we record what the user has said (as best we can tell)
+\newtoks\sseq@thiscalltoks
+
+% These guys are set up because I want \sseq@DeclareDocumentCommandAs@setinputline to work
+% but I want the definition of \sseq@inputline to be local, and it would be really bad to
+% open a group inside of sseq@DeclareDocumentCommandAs that has to be closed in the code.
+% So what I do is globally put it into \sseq@globalinputline using \sseq@setinputline
+% and then after starting the group at the beginning of the actual macro body,
+% call \sseq@loadinputline
+\def\sseq@setinputline{\xdef\sseq@globalinputline{\the\inputlineno}}
+\def\sseq@loadinputline{\let\sseq@inputline\sseq@globalinputline}
+
+\def\sseq@thiscall{\the\sseq@thiscalltoks}
+\def\sseq@setthiscall#1{\sseq@seterrorannotation{call}\sseq@thiscalltoks{#1}}
+\def\sseq@esetthiscall#1{\sseq@seterrorannotation{call}\sseq@eval{\sseq@thiscalltoks{#1}}}
+\def\sseq@gsetthiscall{\sseq@seterrorannotation{call}\global\sseq@thiscalltoks}
+\def\sseq@xsetthiscall#1{\sseq@eval{\@nx\sseq@gsetthiscall{#1}}}
+\def\sseq@xsetlastcall#1{\sseq@eval{\global\sseq@thiscalltoks{#1}}}
+
+\def\sseq@usermacro@esetthiscall#1{\sseq@eval{\sseq@thiscalltoks{#1}}} % Don't set \sseq@error@annotation@call
+
+\newtoks\sseq@foreachcall
+\newtoks\sseq@topofstacktrace
+\sseq@topofstacktrace{}
+\def\sseq@restofstacktrace{}
+
+\def\sseq@pushstacktrace#1{
+ \edef\sseq@temp{\the\sseq@topofstacktrace}
+ \ifx\sseq@temp\pgfutil@empty\else
+ \edef\sseq@restofstacktrace{\unexpanded{\\\ \ \ }\@nx\sseq@stack@unexpanded{\the\sseq@topofstacktrace}\unexpanded\@xp{\sseq@restofstacktrace}}
+ \fi
+ \sseq@eval{\sseq@topofstacktrace{\unexpanded{#1}\sseq@possibleoninputlinedash}}
+ \let\sseq@possibleoninputlinedash\empty
+ \let\sseq@possibleoninputlinepar\empty
+}
+\def\sseq@opushstacktrace#1{\@xp\sseq@pushstacktrace\@xp{#1}}
+\def\sseq@stack@unexpanded#1{\@nx\sseq@stack@unexpanded\unexpanded{{#1}}}
+
+
+
+% Positional information about the error:
+% Lengths are five more than the correct length to account for the fact that the line starts with !<space> and to accommodate two quote characters and a comma.
+\def\sseq@error@annotation@aftercall{\\\\Error occurred after call\sseq@printthiscall\sseq@error@inforeachloop}
+\chardef\sseq@error@annotation@length@aftercall=34
+\def\sseq@error@annotation@call{\\\\Error occurred during call\sseq@printthiscall\sseq@error@inforeachloop}
+\chardef\sseq@error@annotation@length@call=33
+\def\sseq@error@annotation@atbeginforeach{\\\\Error occurred at the beginning of \string\foreach\ statement\sseq@printthiscall\sseq@error@inforeachloop}
+\chardef\sseq@error@annotation@length@atbeginforeach=60
+\def\sseq@error@annotation@atbeginusercmd{\\\\Error occurred at the beginning of user command\sseq@printthiscall\sseq@error@inforeachloop}
+\chardef\sseq@error@annotation@length@atbeginusercmd=54
+
+\def\sseq@seterrorannotation@drawing#1#2#3#4#5{%
+ \def\sseq@error@annotation{\\\\Error occurred while drawing class (#2,#3,#4)\ifnum#5>0 \space which has been replaced #5 time\ifnum#5>1s\fi\fi.
+ Look for a \ifnum#5>0\string\replaceclass\else\string\class\fi\space command that makes a class at that position.
+ Also make sure to check any \@nx\classoptions commands that could be the culprit.}%
+}
+
+\def\sseq@seterrorannotation#1{%
+ \@xp\let\@xp\sseq@error@annotation\csname sseq@error@annotation@#1\endcsname
+ \@xp\let\@xp\sseq@error@annotation@length\csname sseq@error@annotation@length@#1\endcsname
+}
+
+\sseq@seterrorannotation{aftercall}
+
+\def\sseq@inputline{\the\inputlineno}
+\def\sseq@error@inforeachloop{\sseq@possibleoninputlinepar\sseq@possibleperiod}
+\def\sseq@error@inforeachloop@active{\sseq@possiblecomma in loop with variable values:\sseq@for@printvars\sseq@possibleperiodb}
+\def\sseq@error@setup@errorinfo{}% Changed during \foreach and user commands
+
+\def\sseq@possibleoninputlinepar{ (on input line \sseq@inputline)} % let this to a no-op at beginning of usermacros
+\def\sseq@possibleoninputlinedash{ -- on input line \sseq@inputline}
+
+\def\sseq@possiblecomma{,\space}
+\def\sseq@possibleperiod{.}
+\def\sseq@possibleperiodb{.}
+
+
+\def\sseq@error@announcestacktrace{\\\\Stack trace:}
+\def\sseq@printstacktrace{ % Only prints rest of stack trace because top element is mentioned in annotation.
+ \ifx\sseq@restofstacktrace\pgfutil@empty\else
+ \sseq@error@announcestacktrace\sseq@restofstacktrace
+ \fi
+}
+
+% \sseq@error@setup happens before the error is reported, \sseq@error@cleanup at the end.
+
+\ExplSyntaxOn
+
+\def\sseq@error@setup@withinfo{
+ \bgroup
+ \def\foreach{\@nx\foreach}
+ \sseq@countthiscall
+ \ifnum\sseq@thiscallnumchars>\numexpr\l_iow_line_count_int-\sseq@error@annotation@length\relax
+ \def\sseq@printthiscall{:\\\@xp\iow_indent:n\@xp{\the\sseq@thiscalltoks}\\}
+ \let\sseq@possiblecomma\empty
+ \let\sseq@possibleperiod\empty
+ \fi
+ \let\sseq@stack@unexpanded\unexpanded
+ \sseq@error@setup@errorinfo
+ \sseq@error@hook
+}
+
+\def\sseq@error@setup@noinfo{\bgroup\def\foreach{\@nx\foreach}\let\sseq@stack@unexpanded\unexpanded\def\sseq@error@annotation{}\sseq@error@hook}
+\let\sseq@error@setup\sseq@error@setup@noinfo
+\def\sseq@error@cleanup{\egroup\sseq@errortrue} % If the error was thrown in NewDocumentCommand, we need to process it. Just used in sseqmacromakers.
+
+
+\def\sseq@printthiscall{\ "\the\sseq@thiscalltoks"}
+% make \exp_args:NNNf:
+\let \sseq_dummy:NNn \empty
+\cs_generate_variant:Nn \sseq_dummy:NNn {NNf}
+
+
+% Count the chars in thiscall
+% Detokenize handles the control words, but keeps the spaces as spaces. To count them properly, we lowercase them into 0's (any nonspace character would do just fine).
+\def\sseq@countthiscall{
+ \bgroup\lccode`\ =`\0\relax
+ \edef\temp{\detokenize\@xp{\the\sseq@thiscalltoks}}
+ \exp_args:Nf\lowercase{
+ \exp_args:NNNf\egroup
+ \edef\sseq@thiscallnumchars{
+ \exp_args:NNo\@nx\tl_count:n{\temp}
+ }
+ }
+}
+
+\ExplSyntaxOff
+
+
+\def\sseq@error@hook{} % \sseq@error@hook is just used once in the isalive error handling to set \protect to relax. Probably a better way to do that...
+\def\sseq@errorinfo{%
+ \sseq@error@annotation
+ \sseq@printstacktrace
+}
+
+
+
+\def\sseq@atbeginusermacro@msgsetup{%
+ \let\protect\empty % prevent issues if protected variables like \xmax are used as the loop max
+ \let\sseq@error@setup@errorinfo\sseq@error@setup@errorinfo@active % Install foreach error annotations
+ \let\sseq@error@setup\sseq@error@setup@withinfo
+ \sseq@opushstacktrace{\the\sseq@thiscalltoks}
+}
+
+\def\sseq@atbeginforeach@msgsetup{%
+ \let\protect\empty % prevent issues if protected variables like \xmax are used as the loop max
+ \let\sseq@error@setup@errorinfo\sseq@error@setup@errorinfo@active
+ \let\sseq@error@build@foreachvarsinfo\sseq@error@build@foreachvarsinfo@active % Install stack annotation
+ \let\sseq@error@inforeachloop\sseq@error@inforeachloop@active % install message indicating foreach variable values
+ \let\sseq@error@setup\sseq@error@setup@withinfo
+ \sseq@foreachcall\@xp{\string\foreach} % Stack gets pushed at the beginning of the body of the foreach loop once we know what the call looks like
+}
+
+
+\def\sseq@error@setup@errorinfo@active{%
+ \edef\tempa{\the\sseq@thiscalltoks}\edef\tempb{\the\sseq@foreachcall}
+ \ifx\tempa\tempb % We're at the beginning of a for loop
+ \sseq@seterrorannotation{atbeginforeach}
+ \else
+ \edef\tempb{\the\sseq@topofstacktrace}
+ \ifx\tempa\tempb % we're at the beginning of a user macro
+ \sseq@seterrorannotation{atbeginusercmd}
+ \else
+ % The top of the stack trace doesn't get included in the stack trace because it is assumed to be
+ % mentioned in the annotation. Since we aren't at the beginning of the foreach or usercmd,
+ % that means it won't be mentioned. Push an empty thing onto the stack so the innermost \foreach or usercommand is mentioned.
+ \sseq@pushstacktrace{}
+ \fi
+ \fi
+}
+
+%%
+%% \sseq@checkend
+%%
+\def\sseq@checkend#1{
+ \def\sseq@tempa{#1}
+ \ifx\sseq@tempa\@currenvir\else % mismatch
+ \sseq@error@n{endenv-mismatch}{#1}
+ \csname end\@currenvir\endcsname % run the \end code for the correct environment
+ \def\@currenvir{#1} % Prevent \@checkend from throwing a second redundant error
+ \@xp\sseq@break % don't run the code \end for this environment
+ \fi
+}
+
+
+
+
+
+%%%
+%%% Set up l3msg Hooks
+%%%
+
+
+\ExplSyntaxOn
+\char_set_catcode_space:N\ % Needed to make pretocmd work -- there are always spaces after every command, and they need to be retokenized as spaces
+
+\def\SseqErrorToWarning#1{\msg_redirect_name:nnn{spectralsequences}{#1}{warning}}
+\let\sseqerrortowarning\SseqErrorToWarning
+\def\sseq@quieterror#1{\msg_redirect_name:nnn{spectralsequences}{#1}{none}}
+
+\def\sseq@error {\msg_error:nn {spectralsequences}}
+\def\sseq@error@n {\msg_error:nnn {spectralsequences}}
+\def\sseq@error@nn {\msg_error:nnnn {spectralsequences}}
+\def\sseq@error@nnn {\msg_error:nnnnn {spectralsequences}}
+\def\sseq@error@nnnn{\msg_error:nnnnnn{spectralsequences}}
+\def\sseq@error@x {\msg_error:nnx {spectralsequences}}
+\def\sseq@error@xx {\msg_error:nnxx {spectralsequences}}
+\def\sseq@error@xxx {\msg_error:nnxxx {spectralsequences}}
+\def\sseq@error@xxxx{\msg_error:nnxxxx{spectralsequences}}
+\def\sseq@warning {\msg_warning:nn{spectralsequences}}
+
+\def\sseq@newerror#1#2{\msg_new:nnn{spectralsequences}{#1}{#2\sseq@errorinfo}}
+\def\sseq@newerror@internal#1#2{\msg_new:nnn{spectralsequences}{#1}{Internal error: #2 \sseq@contactpackageauthor}}
+\edef\sseq@contactpackageauthor{Contact package author \sseq@authorname\space at \sseq@authoremail.}
+
+
+\cs_new_eq:NN \__sseq_hooked_msg_see_documentation_text:n \msg_see_documentation_text:n
+\cs_new_eq:NN \__sseq_hooked_msg_fatal_code:nnnnnn \__msg_fatal_code:nnnnnn
+\cs_new_eq:NN \__sseq_hooked_msg_error_code:nnnnnn \__msg_error_code:nnnnnn
+\cs_new_eq:NN \__sseq_hooked_msg_warning_code:nnnnnn \__msg_warning_code:nnnnnn
+\cs_new_eq:NN \__sseq_hooked_msg_info_code:nnnnnn \__msg_info_code:nnnnnn
+\cs_new_eq:NN \__sseq_hooked_interupt_wrap:nnn \__msg_interrupt_wrap:nnn
+
+
+\cs_new:Npn \sseq@installmsghooks {%
+ \cs_set_eq:NN \errmessage \sseq@errmessage
+ \cs_set_eq:NN \msg_see_documentation_text:n \__sseq_hooked_msg_see_documentation_text:n
+ \cs_set_eq:NN \__msg_fatal_code:nnnnnn \__sseq_hooked_msg_fatal_code:nnnnnn
+ \cs_set_eq:NN \__msg_error_code:nnnnnn \__sseq_hooked_msg_error_code:nnnnnn
+ \cs_set_eq:NN \__msg_warning_code:nnnnnn \__sseq_hooked_msg_warning_code:nnnnnn
+ \cs_set_eq:NN \__msg_info_code:nnnnnn \__sseq_hooked_msg_info_code:nnnnnn
+ \cs_set_eq:NN \__msg_interrupt_wrap:nnn \__sseq_hooked_interupt_wrap:nnn
+}
+
+\sseq@newerror@internal{msg-patch-failed}{Failed to patch the latex 3 message system. The errors will not be as expressive.}
+
+\def\sseq@msgpatchfailed{
+ \msg_warning:nn{spectralsequences}{msg-patch-failed}
+ \def\sseq@installmsghooks{}
+ \sseq@break
+}
+
+\let\sseq@errmessage@std\errmessage
+\def\sseq@errmessage#1{\sseq@error@setup\let\\\iow_newline:\sseq@errmessage@std{#1\sseq@error@annotation}\sseq@error@cleanup}
+
+\pretocmd\__sseq_hooked_msg_see_documentation_text:n{\sseq@errorinfo}{}{\sseq@msgpatchfailed}
+
+
+\pretocmd\__sseq_hooked_msg_fatal_code:nnnnnn {\sseq@error@setup} {}{\sseq@msgpatchfailed}
+\pretocmd\__sseq_hooked_msg_error_code:nnnnnn {\sseq@error@setup} {}{\sseq@msgpatchfailed}
+%\pretocmd\__sseq_hooked_msg_warning_code:nnnnnn{\sseq@msg@setup} {}{\sseq@msgpatchfailed}
+%\pretocmd\__sseq_hooked_msg_info_code:nnnnnn {\sseq@msg@setup} {}{\sseq@msgpatchfailed}
+\apptocmd\__sseq_hooked_msg_fatal_code:nnnnnn {\sseq@error@cleanup}{}{\sseq@msgpatchfailed}
+\apptocmd\__sseq_hooked_msg_error_code:nnnnnn {\sseq@error@cleanup}{}{\sseq@msgpatchfailed}
+%\apptocmd\__sseq_hooked_msg_warning_code:nnnnnn{\sseq@error@cleanup}{}{\sseq@msgpatchfailed}
+%\apptocmd\__sseq_hooked_msg_info_code:nnnnnn {\sseq@error@cleanup}{}{\sseq@msgpatchfailed}
+\patchcmd \__sseq_hooked_interupt_wrap:nnn {\str_count:N\l__msg_text_str-\str_count:N\l__msg_name_str +2} {2} {}{\sseq@msgpatchfailed}
+
+\sseq@breakpoint
+
+
+
+%%%%
+%%%%
+%%%% Error message definitions
+%%%%
+%%%%
+
+
+%%
+%%
+%% checkdefinitions -- patch-failed errors
+%%
+%%
+\@ifpackagelater{tikz}{2015/07/08}{
+ \sseq@newerror@internal{pgfkeys-patch-failed}{Failed to patch the key value system. Setting all commands to do nothing and quitting. Your copy of tikz is newer than the version spectralsequences was designed for and seems to be incompatible with core features.}
+ \sseq@newerror@internal{fit-patch-failed}{Failed to patch fit. \@nx\circleclasses won't work. Your copy of tikz is newer than the version spectralsequences was designed for and seems to be incompatible with \string\circleclasses.}
+ \sseq@newerror@internal{foreach-patch-failed}{Failed to patch \string\foreach. The errors spectralsequences provides will be less comprehensive. Your copy of tikz is newer than the version spectralsequences was designed for and seems to be incompatible with parts of the error handling system.}
+ \sseq@newerror@internal{circleclasses-not-provided}{\@nx\circleclasses won't work because your copy of tikz is too new. Instead I'm doing nothing.}
+}{
+ \sseq@newerror{pgfkeys-patch-failed}{Failed to patch the key value system. Setting all commands to do nothing and quitting. Your copy of tikz is older than the version spectralsequences was designed for and seems to be incompatible with core features. Update tikz!}
+ \sseq@newerror{fit-patch-failed}{Failed to patch fit. \@nx\circleclasses won't work. Your copy of tikz is older than the version spectralsequences was designed for and seems to be incompatible with \string\circleclasses. Update tikz!}
+ \sseq@newerror{foreach-patch-failed}{Failed to patch \string\foreach. The errors spectralsequences provides will be less comprehensive. Your copy of tikz is older than the version spectralsequences was designed for and seems to be incompatible with parts of the error handling system.}
+ \sseq@newerror{circleclasses-not-provided}{\@nx\circleclasses won't work because your copy of tikz is too old. Instead I'm doing nothing. Update tikz!}
+}
+
+
+\def\sseq@pgfkeyspatchfailed {
+ \msg_critical:nn { spectralsequences } { pgfkeys-patch-failed }
+}
+
+\sseq@newerror@internal{expl3-incompatibility}{Since this version of spectralsequences, the expl3 definition of #1 was updated in an incompatible way.}
+
+\sseq@newerror@internal{this-shouldnt-happen}{This branch of the code should be unreachable. Something really weird has happened. Error occurred in #1.}
+
+% Convexity thing
+\sseq@newerror{clip-not-convex}{The clip you provided to the "clip" option was not convex and the current edge intersects it more than two times. I'm not going to draw it.}
+
+
+%%
+%%
+%% loadstore
+%%
+%%
+\sseq@newerror@internal{illegal-add-background-mode}{\string#1 occurred in background mode.}
+
+
+
+%%
+%%
+%% macromakers
+%%
+%%
+\sseq@newerror@internal{U-xparse-incompatible}{The 'U' argument type is incompatible with the current version of xparse.}
+\sseq@newerror{wont-override-builtin}{Macro '#1' is a spectralsequences built in and I won't override it. Try a different name.}
+\sseq@newerror{usermacro-not-free}{Macro '#1' has already been defined using \string\DeclareSseqCommand, \string\NewSseqCommand, \string\DeclareSseqGroup, or \string\NewSseqGroup. I am overriding it. If this is what you intended, you should either use \@nx\DeclareSseqGroup or \string\DeclareSseqCommand, which won't complain.}
+
+\sseq@newerror{macro-patch-failed}{Failed to patch macro '#1'. This is probably some annoying catcode issue. Try \@nx\tracingpatches to see more information about why the patch failed.}
+\sseq@newerror{usermacro-unsupported-argument-type}{Unsupported argument type '#1'. All argument types in the 2017/05/29 release of xparse are supported except for v, e, and E.}
+
+%%
+%%
+%% parsers
+%%
+%%
+\sseq@newerror{classpattern-already-defined}{Class pattern '#1' already defined. Overwriting it.}
+\sseq@newerror{classpattern-invalid-math-expression}{Expression '#1' encountered in class pattern definition is not a valid math expression. Skipping the definition.}
+\sseq@newerror{classpattern-unexpected-token}{Unexpected token '#1' in class pattern definition. Skipping the definition.}
+\sseq@newerror{classpattern-missing-tokens}{Incomplete class pattern definition. Skipping the definition.}
+\sseq@newerror{classpattern-extra-coord-ignored}{Extra coordinate '#1' ignored in row #2. You probably forgot a semicolon.}
+\sseq@newerror{classpattern-too-few-coords}{Too few coordinates in row #1.}
+
+\sseq@newerror{classpattern-too-many-classes}{%
+ Too many classes in position #1. You are trying to add a #2th class, but the current class pattern is "#3" which only defines offset patterns for up to #4 classes.
+ If you need to have this many classes in this position, you can make your own class pattern with \string\sseqnewclasspattern, but you might want to consider alternatives like using one class to represent multiple generators in some way -- it is hard to make a diagram look good with large numbers of classes in one position.%
+}
+
+\sseq@newerror{invalid-coordinate}{Invalid #1coordinate\sseq@printforerrortype.}
+\sseq@newerror{no-classes}{No classes yet exist in #1\sseq@printerrortype{, the }.}
+\sseq@newerror{index-too-large}{Index '#1' provided\sseq@printforerrortype\ is too large -- only #2 classes exist at position #3.}
+\sseq@newerror{undefined-tag}{Index '#1' provided\sseq@printforerrortype\ is not a valid integer expression or known tag in position #2.}
+\sseq@newerror{class-no-tag}{The class #1 has no tag so I cannot get it.}
+
+\sseq@newerror{class-extra-coords}{Too many coordinates. Ignoring the extra ones.}
+\sseq@newerror{named-coordinate-class}{You can't pass a named coordinate to \string\class.}
+
+\sseq@newerror{d-no-degree}{%
+ If you want to use the differential command \string\d\ you must either specify the grading of the differential or explicitly provide a target coordinate for the differential.
+ Specify the degree by providing 'degree={f(\#1)}{g(\#1)}' as an option to '\string\begin{sseqdata}',
+ where f(\#1) and g(\#1) are formulas for the x and y degree of the \#1th page differential. Alternatively, you may say e.g., '\string\d2(source)(target)'%
+}
+\sseq@newerror{d-named-coord-two-indexes}{You provided a named class and two indexes, but the named class already comes with an index so you can only provide one more for the target. Ignoring the second index.}% should be a warning
+\sseq@newerror{d-target-index-target-coord}{You provided both a target index and a target coordinate, but this overdetermines the differential. Ignoring the target index.}
+\sseq@newerror{d-wrong-degree}{Differential does not respect grading. The target should be in position #1 but instead it is #2 in position #3.}
+
+\sseq@newerror{index-ignored} {The coordinate #1 is not a class, but it has an index '#3'. Ignoring the index and treating it as the literal position #2.}
+\sseq@newerror{anchor-ignored} {The coordinate #1 is not a class, but it has an anchor '#3'. Ignoring the anchor and treating it as the literal position #2.}
+\sseq@newerror{invalid-tikz-coord}{The coordinate #1 does not refer to a tikz class but its #2 coordinate is not valid \string\pgfmathparse\ input.}
+
+% handlers
+\sseq@newerror{NormalizeMonomial-invalid-exponent}{The exponent #1 of the variable #2 in the monomial #3 is not a valid integer expression. Giving up on processing this monomial -- I will leave it alone and hope for the best.}
+\sseq@newerror{AHSSNameHandler-missing-cell}{The name #1 has no cell (an integer expression surrounded by brackets). Giving up on processing it and returning my input in \string\result.}
+\sseq@newerror{AHSSNameHandler-invalid-integer}{The cell degree #1 of #2 is not a valid integer expression. I am leaving it alone, but I processed the monomial.}
+
+
+
+%%
+%%
+%% foreach
+%%
+%%
+\sseq@newerror{DoUntil-no-bound}{You used #1 but specified neither an x range nor a y range.}
+\sseq@newerror{DoUntil-no-progress}{You used #1 but the loop body you provided seems to be making no progress towards the boundary.}
+\sseq@newerror{Do-invalid-int-expr}{Argument "#2" to #1 is not a valid integer expression.}
+
+
+
+
+%%
+%%
+%% keys
+%%
+%%
+
+\def\sseq@possiblecurrentvalue{\ifx\pgfkeyscurrentvalue\pgfkeysnovalue@text\else=\pgfkeyscurrentvalue\fi}
+\def\sseq@possiblecurrentvaluemean{\ifx\pgfkeyscurrentvalue\pgfkeysnovalue@text\else={\@xp\sseq@macrogobble\meaning\pgfkeyscurrentvalue}\fi}
+
+\sseq@newerror{option-illegal}{%
+ You said '#1\sseq@possiblecurrentvalue', but the option '#1' is
+ illegal in the sseqdata and sseqpage environments#3.%
+}
+\sseq@newerror{option-illegal-as-global}{%
+ You passed '#1\sseq@possiblecurrentvalue' as a global option, but option '#1' is illegal as a global option.%
+}
+\sseq@newerror{option-global-only}{%
+ You said '#1\sseq@possiblecurrentvaluemean', but the option '#1' is
+ only allowed as a global option (as in '\string\begin{sseqdata}[#1\sseq@possiblecurrentvaluemean]'
+ or '\string\begin{sseqpage}[#1\sseq@possiblecurrentvaluemean]').%
+}
+
+% Applies to name and page. Saying "sseqs={name=somename}" is a bad idea.
+\sseq@newerror{option-no-default-allowed}{%
+ You cannot set a default value for '#1'.% Try \@nx\sseqset{sseqs={#1\ifx\pgfkeyscurrentvalue\pgfkeysnovalue@text\else=\pgfkeyscurrentvalue\fi}}.%
+}
+\sseq@newerror{option-tikz-prims-only}{%
+ You can only use '#1' as an option for tikz primitives, not for \@nx\class, \@nx\replaceclass, \@nx\classoptions, \@nx\d, \@nx\doptions, \@nx\structline, or \@nx\structlineoptions.%
+}
+
+\sseq@newerror{invalid-intexpr}{The value '#2' provided for key '#1' is not a valid integer expression.}
+\sseq@newerror{invalid-positive-intexpr}{The value '#2' provided for key '#1' does not evaluate to a positive integer.}
+\sseq@newerror{invalid-dimenexpr}{The value '#2' provided for key '#1' is not a valid dimension expression.}
+\sseq@newerror{invalid-color}{The value '#2' provided for key '#1' is not a valid color or color expression.}
+\sseq@newerror{invalid-axis-type}{The value '#1' provided for '\sseq@axistypekey{#2}' is not valid. Valid values for '\sseq@axistypekey{#2}' are: \sseq@validaxestypes}
+
+\sseq@newerror{det-one-after-scale}{You cannot use the option '#1\ifx\pgfkeyscurrentvalue\pgfkeysnovalue@text\else=\pgfkeyscurrentvalue\fi' as a global option after using 'xscale', 'yscale', or 'scale'. Please move '#1\ifx\pgfkeyscurrentvalue\pgfkeysnovalue@text\else=\pgfkeyscurrentvalue\fi' before any scaling options.}
+\sseq@newerror{invalid-scale}{You said '#1scale=#2' as a global option, but '#2' is not a valid pgf math expression.}
+\sseq@newerror{unit-scale}{You said '#1scale=#2' as a global option, but '#2' is not a scalar.}
+\sseq@newerror{negative-scale}{You said '#1scale=#2' as a global option, but scaling by negative numbers is not allowed as a global option. Instead, please use the option '#1mirror', and then scale by the absolute value.}
+
+\sseq@newerror{illegal-transform}{%
+ You said '#1\ifx\pgfkeyscurrentvalue\pgfkeysnovalue@text\else=\pgfkeyscurrentvalue\fi', but
+ no coordinate transforms are allowed in the body of sseqpage or sseqdata other than shifts by unit-free integers
+ except as options for tikz primitives or for scopes that only contain tikz primitives. If you need a coordinate transformation,
+ first use the option 'standard parse'.%
+}
+
+\sseq@newerror{unknown-key}{I do not know the key "\pgfkeyscurrentname".}
+\sseq@newerror{unknown-global}{Undefined global option '\pgfkeyscurrentname\@xp\ifx\pgfkeyscurrentvalue\pgfkeysnovalue\else=\pgfkeyscurrentvalue\fi'.}
+
+\sseq@newerror{scope-quotes}{Quotes are not allowed as options to a scope.}
+\sseq@newerror{tikz-edge-quotes}{If you want to use quotes on a tikz edge, please provide it directly to the 'to' command e.g., \@nx\draw (0,0) to["hi"] (1,0);}
+
+\sseq@newerror{change-style-data}{It's pointless to use 'change #1s' in the sseqdata environment. Try '#1s={##1}' instead.}
+\sseq@newerror{change-style-standalone}{It's pointless to use 'change #1s' in a standalone sseqpage environment. Try '#1s={##1}' instead.}
+\sseq@newerror{page-update-existing}{Option 'update existing' is only for the sseqdata environment.}
+\sseq@newerror{page-no-name}{You can't specify a page but no name.}
+\sseq@newerror{keep-changes-no-name}{Option 'keep changes' is only for named spectral sequences.}
+\sseq@newerror{keep-changes-sseqdata}{Option 'keep changes' is only for the sseqpage environment.}
+\sseq@newerror{unknown-grid}{Undefined grid style '#1'.}
+\sseq@newerror{unknown-class-pattern}{Unknown class pattern '\sseq@classpattern'.}
+\sseq@newerror{class-placement-shift}{Shifts are illegal in the transformations applied in "class placement" key. If you want to shift your nodes, use \string\sseqnewclasspattern.} % #1 <- x shift, #2 <- shift amount
+\sseq@newerror@internal{do-command-failed}{\string\sseq@pgfkeysdocommand{#1}{#2} failed.}
+
+\sseq@newerror{standard-parse}{You cannot use \string#1 inside a scope that has either the background key or coordinate transformations other than integer shifts.}
+
+\sseq@newerror{no-tooltip}{You cannot use the 'tooltip' option without either using the 'tooltips' package option or giving your own definition for '\string\sseqtooltip'.
+ Tooltips aren't enabled by default because they use the 'pdfcomment' package which creates two extra auxiliary files.%
+}
+
+%%
+%%
+%% main
+%%
+%%
+
+\sseq@newerror{endenv-mismatch}{\string\begin {\@currenvir }\@currenvline \space ended by \string\end {#1}. I'm going to run the code for \string\end{\@currenvir} instead.}
+\sseq@newerror{env-inside-tikz}{#1 environment started inside tikz.}
+\sseq@newerror{env-inside-sseq}{#1 environment started inside other sseq environment.}
+\sseq@newerror{env-data-no-name}{Every 'sseqdata' environment must have a name. Specify one by saying 'name=your sseq name'.}
+\sseq@newerror{env-data-already-exist}{A spectral sequence with the name '#1' already exists; updating it. If you want to add to an already existing spectral sequence, please pass the option 'update existing'.} % warning
+\sseq@newerror{env-page-not-exist}{No spectral sequence named '#1' exists. Making a new one.} % warning
+
+
+\sseq@newerror{is-alive-illegal-here}{You can't use \@nx\isalive outside of a page constraint for a tikz primitive.}
+
+\sseq@newerror{no-range}{Empty spectral sequence. For every spectral sequence you must either specify both the "x range" and "y range" or include at least one
+\string\class.} % maybe warning?
+\sseq@newerror{range-overflow}{The #1 range #2 is too large. The maximum #1 range to fit on the page with the current #1scale and plot options is #1max - #1min = #3. The maximum #1scale possible with the current #1range is #4. Either change the scale or the range as appropriate.}
+\sseq@newerror{range-super-overflow}{The #1 range #2 is too large. The maximum range possible is #1max - #1min = 16383 because of internal limitations of etex. }
+
+\sseq@newerror{index-out-of-bounds}{Index #1 is greater than or equal to the current stack depth #2. Valid indices must be strictly less than the stack depth.}
+\sseq@newerror{incomplete-tikz}{Incomplete tikz expression. What I have so far is #1 and I am stopping because I ran into #2. You probably forgot a semicolon.}
+
+
+\sseq@newerror{cmdoptions-only}{You can only use the '#1' option with #2, not with #3.} % warning
+\sseq@newerror{cmdoptions-without-options}{There is no reason to use \string#1 without giving options.} % warning
+\sseq@newerror{cmdoptions-feature-doesn't-exist}{There is no #1 of the form '\sseq@thiscall' so I can't add options to it.}
+
+\sseq@newerror{classoptions-already-dead}{Generation #1 of the class #2 perished on page #3 so there is no reason to add an option to it on page \sseq@thepage.}
+\sseq@newerror{classoptions-not-yet-born}{Generation #1 of the class #2 is not yet present on page \sseq@thepage\ so there is no reason to add an option to it.}
+\sseq@newerror{classoptions-page-gen}{Don't use both "page" and "generation" for the same \@nx\classoption command.} % warning
+\sseq@newerror{classoptions-class-already-named}{The class \coord\ already is named '\sseq@obj{class.\classname.name}'. Overwriting it. Use \@nx\nameclass if you want to give it a second name.}%warning
+\sseq@newerror{doptions-invisible}{Cannot add options to invisible differential.}
+\sseq@newerror{doptions-no-effect-on-this-page}{Adding an option to a differential on page #1 when the current page is \sseq@thepage\ and the 'keep changes' option is not present has no effect.}
+
+\sseq@newerror{class-tag-already-defined}{A class with tag '#1' is already defined in position #2. Overwriting it.} % warning
+
+\sseq@newerror{replaceclass-not-dead-yet}{I cannot replace the class at #1 because it seems to be alive and well.}
+\sseq@newerror{replaceclass-no-effect-on-this-page}{The class at #1 died on page #2 so replacing it only affects pages after #3, but the current page is \sseq@thepage.} % warning
+\sseq@newerror{replacestructlines-class-not-replaced}{I cannot replace the structlines on #1 because it has not been replaced.}
+
+
+\sseq@newerror{d-invalid-page}{Invalid page '#1'.}
+\sseq@newerror{d-class-already-hit}{% #1 <-#3, #2 <-\csname #3name\endcsname, #3 <- #2, #4 <- \unexpanded\@xpthree{\sseq@obj{\sseq@temp.deathinfo}}
+ The #1 #2 of differential \sseq@thiscall\ on page #3 has already been the #4.
+ Use \string\replaceclass#2 before \sseq@thiscall.%
+}
+\sseq@newerror{d-class-has-extension}{The #1 #2 of differential \sseq@thiscall\ on page #3 has already been involved in an extension. If you are planning to replace it, move the \string\extension\ after the call to \string\replaceclass.}
+\sseq@newerror{d-hit-same-page-replaceclass}{%#1 <- #3, #2 <- \csname #3name\endcsname, #3 <- #2, #3 <-\sseq@obj{#1[\the\sseq@tempcount].deathinfo},
+ The #1 #2 of differential \sseq@thiscall\ was the #3.
+ If multiple differentials touch a class on the same page, they cannot be separated by a \@nx\replaceclass command.%
+}
+\sseq@newerror{d-hit-wrong-order}{%
+ The #1 #2 of differential \sseq@thiscall\ on page #3 has already been the #4.
+ First use the shorter differential, then \string\replaceclass#2, then the longer differential.%
+}
+
+
+\sseq@newerror{structline-no-page-max}{You cannot provide \@nx\structline with the option "page=min--max", only with "page=min".} %warning
+\sseq@newerror{structline-already-exists}{Cannot add a structline from #1 to #2 because such a structline already exists.}
+\sseq@newerror{structline-stillborn}{#1 dies before it is born. Perhaps you meant to specify a different source or target?}
+\sseq@newerror{extension-stillborn}{Either the source or target of extension is not currently alive. Perhaps you meant to specify a different source or target? Perhaps you forgot a call to \@nx\replaceclass?}
+\sseq@newerror{edge-source-target-equal}{The source and target of a #1 cannot be equal.}
+
+
+\protected\def\sseq@quiet{\msg_redirect_module:nnn{spectralsequences}{error}{info}}
+\protected\def\sseq@endquiet{}
+
+\ExplSyntaxOff
diff --git a/graphics/pgf/contrib/spectralsequences/src/sseqparsers.code.tex b/graphics/pgf/contrib/spectralsequences/src/sseqparsers.code.tex
new file mode 100644
index 0000000000..b7452d89ae
--- /dev/null
+++ b/graphics/pgf/contrib/spectralsequences/src/sseqparsers.code.tex
@@ -0,0 +1,1816 @@
+%%
+%% Package: spectralsequences v1.3.0 2021-07-18
+%% Author: Hood Chatham
+%% Email: hood@mit.edu
+%% Date: 2021-07-18
+%% License: Latex Project Public License
+%%
+%% File: sseqparsers.code.tex
+%%
+%% Defines the parsers and input sanitizers. Also handles tikz primitive parsing and \isalive.
+%% ExplSyntaxOn for this whole file!
+
+\ExplSyntaxOn
+\let\sseq@NoValue\c__xparse_no_value_tl
+% They changed \c__xparse_no_value_tl to \c_novalue_tl at some point. If the first definition doesn't work, try again.
+\@ifundefined{sseq@NoValue}{\let\sseq@NoValue\c_novalue_tl}{}
+
+\protected\def\sseqparseint#1#2{
+ \sseq@ifintexpr{#2+0}{ % +0 here to prevent \lastx from freaking out if it's at the end of #2.
+ \edef#1{\the\numexpr#2\relax}
+ }{
+ \sseq@error{invalid-int-expr}
+ }
+}
+
+% WARNING: doesn't work =(
+\def\sseq@ifint#1{
+ \ifcat$\romannumeral0#1$
+ \prg_return_true:
+ \else
+ \ifcat$\romannumeral-0#1$
+ \prg_return_true:
+ \else
+ \prg_return_false:
+ \fi
+ \fi
+}
+
+% Prevent \protect from causing trouble for \pgfmathparse
+\let\sseq@pgfmathparse@orig\pgfmathparse
+\protected\def\sseq@pgfmathparse#1{%
+ \bgroup
+ \let\@@protect\protect
+ \def\protect{}%
+ \let\pgfmath@protected@edef\edef
+ \sseq@pgfmathparse@orig{#1}%
+ \restore@protect
+ \sseq@smuggle@macro\pgfmathresult
+ \egroup
+}
+
+
+%%% ifpgfmathexpr
+% #1 -- expression to test
+% #2 -- true case
+% #3 -- false case
+% This tests true if \pgfmathparse{#1} throws an error or not. Luckily, \pgfmathparse pipes its errors through \pgfmath@error so this is easy.
+\def\sseq@ifpgfmathexpr#1{
+ \begingroup
+ \let\protect\pgfutil@empty
+ \global\sseq@gtempiftrue
+ \def\pgfmath@error##1##2{\global\sseq@gtempiffalse\sseq@closegroups\sseq@break}
+ \def\sseq@groupflag{}
+ \pgfmathparse{#1}
+ \global\let\ifpgfmathunitsdeclared\ifpgfmathunitsdeclared
+ \ifpgfmathunitsdeclared
+ \xdef\sseq@mathresult{\pgfmathresult pt}
+ \else
+ \xdef\sseq@mathresult{\pgfmathresult}
+ \fi
+ \endgroup
+ \sseq@breakpoint
+ \ifsseq@gtempif
+ \@xp\@firstoftwo
+ \else
+ \@xp\@secondoftwo
+ \fi
+}
+
+\def\sseq@closegroups{\ifx\sseq@groupflag\empty\endgroup\@xp\sseq@closegroups\fi}
+
+
+% Test if single token input is a digit
+\def\sseq@ifdigit#1{
+ \ifodd0
+ \ifx#1 0 1 \else
+ \ifx#1 1 1 \else
+ \ifx#1 2 1 \else
+ \ifx#1 3 1 \else
+ \ifx#1 4 1 \else
+ \ifx#1 5 1 \else
+ \ifx#1 6 1 \else
+ \ifx#1 7 1 \else
+ \ifx#1 8 1 \else
+ \ifx#1 9 1 \else
+ \fi \fi \fi \fi \fi
+ \fi \fi \fi \fi \fi
+ \relax
+ \@xp\@firstoftwo
+ \else
+ \@xp\@secondoftwo
+ \fi
+}
+
+%%% ifintexpr
+% #1 -- expresion to test
+% #2 -- true case
+% #3 -- false case
+% This tests true if \numexpr #1\relax throws no error and consumes all of #1 and false otherwise.
+
+% Spaces matter to us, because \sseq@ifintexpr{1 1} is false but \sseqsifintexpr{11} is true
+% so we make spaces into catcode other and use \scantokens
+\def\sseq@fexpsafenil{\@nx\sseq@fexpsafenil}
+
+\def\sseq@ifintexpr#1{%
+ \bgroup
+ \sseq@tempcount\z@
+ \sseq@ifintexpr@{needsint}#1\sseq@fexpsafenil%
+ \@xp\egroup\next
+}
+
+% We need to use \futurelet so that we can detect open braces even when they only surround one token like {1}
+% also we use it to detect spaces. Store the state in \sseq@ifintexpr@state first.
+\def\sseq@ifintexpr@#1{\def\sseq@ifintexpr@state{#1}\futurelet\testtok\sseq@ifintexpr@@}
+\def\sseq@ifintexpr@@{%
+ \ifx\testtok\bgroup%
+ \let\next\sseq@ifintexpr@false
+ \else
+ \ifx\testtok\sseq@fexpsafenil
+ \@xp\let\@xp\next\csname sseq@ifintexpr@@\sseq@ifintexpr@state @done\endcsname
+ \else
+ % We need to check here for a space because \string<space> produces NO OUTPUT regardless of the catcode of the space.
+ % This messes up \sseq@ifintexpr@@@ because it doesn't expect \string#1 to produce no characters.
+ \@xp\ifx\space\testtok
+ \def\next{\sseq@ifintexpr@next{\space}\@xp\next\romannumeral-`0}
+ \else
+ \let\next\sseq@ifintexpr@@@
+ \fi
+ \fi
+ \fi
+ \next
+}
+
+\def\sseq@ifintexpr@@@#1{
+ \ifcat$\@xp\@gobble\string#1$%
+ \@xp\sseq@ifintexpr@@@@\@xp#1
+ \else
+ % This is a macro, so fexpand it
+ % Then use f expansion.
+ \@xp\sseq@ifintexpr@@@fexpcs\@xp#1
+ \fi
+}
+
+\def\sseq@ifintexpr@@@fexpcs{\exp_last_unbraced:Nf\sseq@ifintexpr@@@fexpcs@}
+\def\sseq@ifintexpr@@@fexpcs@{\futurelet\testtok\sseq@ifintexpr@@@fexpcs@@}
+\def\sseq@ifintexpr@@@fexpcs@@{
+ \ifx\testtok\bgroup
+ \@xp\sseq@ifintexpr@false % We already tested for groups above, so we need to check if this expanded to a group
+ \else
+ \@xp\sseq@ifintexpr@@@@ % If it's still a control sequence, then this will fail in the \pgfutil@ifundefined step
+ \fi
+}
+
+
+
+% We can't just use \futurelet because "\let\testtok(" makes \testtok unexpandable
+% (I guess that makes sense, but why is it that I need \@xp\ifx\otherspace above if I've also \let\otherspace to a character? Mysterious...),
+% so then "\csname hello\testtok\endcsname" is an error. This indexes into our state machine,
+% cases: a digits, + or -, * or /, (, ), or something else (anything else always leads to false
+\def\sseq@ifintexpr@@@@#1{%
+ \ifx#1\sseq@fexpsafenil
+ \def\next{\csname sseq@ifintexpr@@\sseq@ifintexpr@state @done\endcsname\sseq@fexpsafenil}%
+ \else
+ \sseq@ifdigit{#1}%
+ {\sseq@ifintexpr@next{digit}}%
+ {%
+ \ifx#1+%
+ \sseq@ifintexpr@next{+-}
+ \else
+ \ifx#1-%
+ \sseq@ifintexpr@next{+-}
+ \else
+ \ifx#1*%
+ \sseq@ifintexpr@next{*/}
+ \else
+ \ifx#1/%
+ \sseq@ifintexpr@next{*/}
+ \else
+ % This extra \string here is so that if a control sequence fexpanded and still gave a control sequence,
+ % we don't get a missing \endcsname error here, it just returns false
+ \pgfutil@ifundefined{sseq@ifintexpr@@\sseq@ifintexpr@state @\string#1}%
+ {\let\next\sseq@ifintexpr@false}%
+ {\sseq@ifintexpr@next{#1}}%
+ \fi
+ \fi
+ \fi
+ \fi
+ }%
+ \fi
+ \next
+}
+
+\def\sseq@ifintexpr@true#1\sseq@fexpsafenil{\ifnum\sseq@tempcount=\z@ \let\next\@firstoftwo\else\let\next\@secondoftwo\fi}
+\def\sseq@ifintexpr@false#1\sseq@fexpsafenil{\let\next\@secondoftwo}
+
+\def\sseq@makeifint#1#2#3{\@xp\def\csname sseq@ifintexpr@@#1@#2\endcsname{#3}}
+\def\sseq@ifintexpr@next#1{\@xp\let\@xp\next\csname sseq@ifintexpr@@\sseq@ifintexpr@state @#1\endcsname}
+
+\sseq@makeifint{needsint}{done}{\sseq@ifintexpr@false}
+\sseq@makeifint{needsint}{digit}{\sseq@ifintexpr@{int}}
+\sseq@makeifint{needsint}{*/}{\sseq@ifintexpr@false}
+\sseq@makeifint{needsint}{+-}{\sseq@ifintexpr@{needsint}}
+\sseq@makeifint{needsint}{(}{\advance\sseq@tempcount\@ne\sseq@ifintexpr@{needsint}}
+\sseq@makeifint{needsint}{)}{\sseq@ifintexpr@false}
+\sseq@makeifint{needsint}{\space}{\sseq@ifintexpr@{needsint}}
+
+\sseq@makeifint{int}{done}{\sseq@ifintexpr@true}
+\sseq@makeifint{int}{digit}{\sseq@ifintexpr@{int}}
+\sseq@makeifint{int}{*/}{\sseq@ifintexpr@{needsint}}
+\sseq@makeifint{int}{+-}{\sseq@ifintexpr@{needsint}}
+\sseq@makeifint{int}{(}{\sseq@ifintexpr@false}
+\sseq@makeifint{int}{)}{
+ \advance\sseq@tempcount\m@ne
+ \ifnum\sseq@tempcount<\z@\relax
+ \@xp\@xp\@xp\sseq@ifintexpr@false\@xp\@gobble
+ \else
+ \@xp\sseq@ifintexpr@
+ \fi{nointallowed}
+}
+\sseq@makeifint{int}{\space}{\sseq@ifintexpr@{nointallowed}}
+
+\sseq@makeifint{nointallowed}{done}{\sseq@ifintexpr@true}
+\sseq@makeifint{nointallowed}{digit}{\sseq@ifintexpr@false}
+\sseq@makeifint{nointallowed}{*/}{\sseq@ifintexpr@{needsint}}
+\sseq@makeifint{nointallowed}{+-}{\sseq@ifintexpr@{needsint}}
+\sseq@makeifint{nointallowed}{(}{\sseq@ifintexpr@false}
+\sseq@makeifint{nointallowed}{)}{
+ \advance\sseq@tempcount\m@ne
+ \ifnum\sseq@tempcount<\z@
+ \@xp\@xp\@xp\sseq@ifintexpr@false\@xp\@gobble
+ \else
+ \@xp\sseq@ifintexpr@
+ \fi{nointallowed}
+}
+\sseq@makeifint{nointallowed}{\space}{\sseq@ifintexpr@{nointallowed}}
+
+%%
+%% This parser defines the syntax for the page argument of \d.
+%%
+%% #1 -- callback. Will pass control to this function when done.
+%% stores output in \sseq@dpage
+%% It also sets the flag \sseq@tempif to be
+%% true if there is an open parenthesis after the page
+%% false if there is no open parentheis -- so no optional argument for \d.
+\def\sseq@d@grabpage#1{
+ \let\sseq@grabdpage@return#1
+ \futurelet\testtok\sseq@grabdpage@
+}
+\def\sseq@grabdpage@{
+ \sseq@tempiftrue
+ \ifx\testtok\bgroup
+ \let\next\sseq@grabdpage@group
+ \else
+ \def\sseq@dpage{}
+ \let\next\sseq@grabdpage@norm
+ \fi
+ \next
+}
+
+\def\sseq@grabdpage@group#1{\def\sseq@dpage{#1}\sseq@grabdpage@return}
+
+% I guess this is "norm" because it's the most basic case.
+% Why did I make this so complicated?
+\def\sseq@grabdpage@norm{
+ \futurelet\testtok\sseq@grabdpage@norm@
+}
+
+% If the next token is a open group or a new paragraph, we're done collecting and there's no parenthetical argument.
+% If the next token is an open paren, we're done collecting and there is a parenthetical argument.
+% Spaces get added to the token list (I guess that's important because it changes arithmetic sometimes? there must have been an issue at some point)
+% Every other token gets handled by \sseq@grabdpage@token
+\def\sseq@grabdpage@norm@{
+ \let\next\sseq@grabdpage@token
+ \ifx\testtok\bgroup % Open group ==> done collecting page, no optional argument
+ \sseq@tempiffalse
+ \let\next\sseq@grabdpage@return
+ \else
+ \ifx\testtok\par % new paragraph ==> done collecting page, no optional argument
+ \sseq@tempiffalse
+ \let\next\sseq@grabdpage@return
+ \else
+ \ifx\testtok( % paren ==> done collecting page, optional argument
+ \let\next\sseq@grabdpage@return
+ \else
+ \@xp\ifx\space\testtok
+ \let\next\sseq@grabdpage@space
+ \fi
+ \fi
+ \fi
+ \fi
+ \next
+}
+
+\def\sseq@grabdpage@space{\sseq@d@addto@macro\sseq@dpage{~}\@xp\sseq@grabdpage@norm\romannumeral-`0}
+
+% All other tokens
+\def\sseq@grabdpage@token#1{
+ \ifcat$\@xp\@gobble\string#1$% % If #1 is a non-macro token, just add it to \sseq@dpage
+ \sseq@d@addto@macro\sseq@dpage{#1}
+ \@xp\sseq@grabdpage@norm
+ \else % Now it's a macor
+ \ifx#1\end % \end ==> done
+ \sseq@tempiffalse
+ \@xp\sseq@grabdpage@return\@xp#1\romannumeral-`0
+ \else
+ \ifx#1\begin % \begin ==> done
+ \sseq@tempiffalse
+ \@xp\sseq@grabdpage@return\@xp#1\romannumeral-`0
+ \else % Otherwise, let's try to expand it.
+ \@xp\sseq@grabdpage@token@fexpcs\@xp#1\romannumeral-`0
+ \fi
+ \fi
+ \fi
+}
+
+\def\sseq@grabdpage@token@fexpcs{\exp_last_unbraced:Nf\sseq@grabdpage@token@fexpcs@}
+\def\sseq@grabdpage@token@fexpcs@{\futurelet\testtok\sseq@grabdpage@token@fexpcs@@}
+\def\sseq@grabdpage@token@fexpcs@@{
+ \ifx\testtok\bgroup
+ \sseq@tempiffalse
+ \@xp\sseq@grabdpage@return
+ \else
+ \@xp\sseq@grabdpage@token@fexpcs@@@
+ \fi
+}
+\def\sseq@grabdpage@token@fexpcs@@@#1{
+ \ifcat$\@xp\@gobble\string#1$% Did it fexpand into a non control sequence token?
+ \sseq@d@addto@macro\sseq@dpage{#1} % If so add it
+ \@xp\sseq@grabdpage@norm
+ \else
+ \sseq@tempiffalse
+ \@xp\sseq@grabdpage@return\@xp#1% Else remember to put the token back if we're not going to use it!!
+ \fi
+}
+
+
+%%
+%%
+%% New Class Pattern
+%%
+%%
+
+\newcount\sseq@cp@row
+\newcount\sseq@cp@n
+
+\def\SseqNewClassPattern#1#2{
+ \ifcsname sseq@#1xoffset1/1\endcsname\sseq@error@n{classpattern-already-defined}{#1}\fi % could be a warning
+ \begingroup
+ \def\sseq@name{#1}
+ \def\sseq@temp{}
+ \def\sseq@state{nocoord}
+ \sseq@cp@row=\@ne
+ \sseq@cp@n=\z@
+ \let\next\sseq@newclasspattern@
+ \sseq@newclasspattern@#2\sseq@nil
+}
+\let\sseqnewclasspattern\SseqNewClassPattern
+
+\def\sseq@newclasspattern@{
+ \@ifnextchar\bgroup{\sseq@error@n{classpattern-unexpected-token}{\{}}{\sseq@newclasspattern@processstate}
+}
+
+\def\sseq@newclasspattern@processstate{
+ \csname sseq@newclasspattern@processstate@\sseq@state\endcsname
+}
+
+\def\sseq@newclasspattern@processstate@xcoord#1{
+ \ifx#1,
+ \sseq@ifpgfmathexpr{\sseq@temp}{
+ \let\sseq@tempx\sseq@mathresult
+ \def\sseq@temp{}
+ \def\sseq@state{ycoord}
+ }{
+ \sseq@error@x{classpattern-invalid-math-expression}{\sseq@temp}
+ \let\next\sseq@newclasspattern@abort
+ }
+ \else
+ \ifx#1\sseq@nil
+ \sseq@error{classpattern-missing-tokens}
+ \let\next\endgroup
+ \else
+ \sseq@d@addto@macro\sseq@temp{#1}
+ \fi
+ \fi
+ \next
+}
+
+\def\sseq@newclasspattern@processstate@ycoord#1{
+ \ifx#1)
+ \sseq@ifpgfmathexpr{\sseq@temp}{% Need iffloatexpr =(
+ \let\sseq@tempy\sseq@mathresult
+ \def\sseq@temp{}
+ \def\sseq@state{nocoord}
+ \advance\sseq@cp@n1\relax
+ \ifnum\sseq@cp@n>\sseq@cp@row\relax
+ \sseq@error@xx{classpattern-extra-coord-ignored}{(\sseq@tempx,\sseq@tempy)}{\the\sseq@cp@row}
+ \else
+ \sseq@e@addto@macro\sseq@newclasspattern@dodefs{
+ \def\@xp\@nx\csname sseq@\sseq@name xoffset\the\sseq@cp@n/\the\sseq@cp@row\endcsname{\sseq@tempx}
+ \def\@xp\@nx\csname sseq@\sseq@name yoffset\the\sseq@cp@n/\the\sseq@cp@row\endcsname{\sseq@tempy}
+ }
+ \fi
+ }{
+ \sseq@error@x{classpattern-invalid-math-expression}{\sseq@temp}
+ \let\next\sseq@newclasspattern@abort
+ }
+ \else
+ \ifx#1\sseq@nil
+ \sseq@error{classpattern-missing-tokens}
+ \let\next\endgroup
+ \else
+ \sseq@d@addto@macro\sseq@temp{#1}
+ \fi
+ \fi
+ \next
+}
+
+\def\sseq@newclasspattern@processstate@nocoord#1{
+ \ifx#1(
+ \def\sseq@state{xcoord}
+ \else
+ \ifx#1;
+ \ifnum\sseq@cp@n<\sseq@cp@row\relax
+ \sseq@error@x{classpattern-too-few-coords}{\the\sseq@cp@row}
+ \let\next\sseq@newclasspattern@abort
+ \else
+ \advance\sseq@cp@row\@ne
+ \sseq@cp@n=\z@
+ \fi
+ \else
+ \ifx#1\sseq@nil
+ \let\next\sseq@newclasspattern@finish
+ \else
+ \sseq@error@n{classpattern-unexpected-token}{#1}
+ \let\next\sseq@newclasspattern@abort
+ \fi
+ \fi
+ \fi
+ \next
+}
+
+\def\sseq@newclasspattern@finish{
+ \ifnum\sseq@cp@n=\z@\relax
+ \advance\sseq@cp@row\m@ne
+ \else
+ \ifnum\sseq@cp@n=\sseq@cp@row\relax\else
+ \sseq@error@x{classpattern-too-few-coords}{\the\sseq@cp@row}
+ \let\sseq@newclasspattern@dodefs\empty
+ \fi
+ \fi
+ % This will make a definition even if the class pattern was rejected because of the too-few-coords error
+ % It doesn't matter though, because we don't use this to check for existence
+ \sseq@e@addto@macro\sseq@newclasspattern@dodefs{%
+ \chardef\@xp\@nx\csname sseq@\sseq@name @maxclasses\endcsname=\the\sseq@cp@row\relax
+ }%
+ \@xp\endgroup
+ \sseq@newclasspattern@dodefs
+
+
+}
+\def\sseq@newclasspattern@dodefs{}
+\def\sseq@newclasspattern@abort#1\sseq@nil{\endgroup}
+
+
+
+
+%%%%%% %%%%%%
+%% %%
+%% Coordinate Parsers %%
+%% %%
+%%%%%% %%%%%%
+
+
+\def\sseq@ifdead#1{\ifnum\sseq@obj{class.#1[\sseq@obj{class.#1.num}].page}<\sseq@infinitycount\@xp\@firstoftwo\else\@xp\@secondoftwo\fi}
+
+
+% These do all of the work of taking a coordinate of the form (x,y), (x,y,n), or (x,y,tag) and turning them
+% into the internal representation needed for the rest of the package.
+% This comes in two parts:
+% \sseq@parsecoord@maincoord -- separates a coordinate into {x,y}{n or tag}, numerically evaluates x and y, and adds in the values of \sseq@x and \sseq@y
+% \sseq@parsecoord@index -- decides which class {n or tag} represents
+%
+% There are several interface commands:
+% \sseq@parsecoord -- for most things
+% \sseq@cparsecoord -- for \class (but not for \replaceclass or \classoptions). Doesn't use \sseq@parsecoord@index at all.
+% \sseq@dparsecoord -- for \d and \doptions
+% \sseq@parsecoordex -- for \isalive: the body of a page constraint needs to be \edef'd so we need a completely expandable version.
+% The expandable version doesn't have any error checking -- to get the error checking, we pass the coordinates once through the unexpandable version
+% inside of an hbox.
+% \sseq@parsecoord@allownonexisting -- doesn't raise an error if the class doesn't exist.
+
+\def\sseq@errortype{}
+\def\sseq@printerrortype#1{\@xp\sseq@ifempty\@xp{\sseq@errortype}{}{#1 \sseq@errortype}}
+\def\sseq@printforerrortype{\sseq@printerrortype{~for~}}
+
+%% Pulls off x,y and evaluates them, puts n or empty into a separate variable.
+% #1 -- a coordinate of the form {x,y} or {x,y,stuff}
+\let\sseq@relax\relax
+% The \sseq@relax is just to make \lastclass work. It has to pull an argument off the token stream to expandably check whether
+% it is an integer. However, without this \sseq@relax, \lastclass could appear as the last token of the edef, which is no good.
+\def\sseq@parsecoord@maincoord#1{\exp_last_unbraced:Nx\sseq@parsecoord@maincoord@{#1\sseq@relax}}
+\def\sseq@parsecoord@maincoord@#1\sseq@relax{%
+ \pgfutil@in@ \sseq@protecterror { #1 }
+ \ifpgfutil@in@
+ \bgroup
+ \sseq@restorefont % This prevents "Missing character" stuff from being written to the log.
+ \let\sseq@protecterror\relax
+ \setbox0=\hbox{#1} % Trigger the error message
+ \egroup
+ \@xp\sseq@break
+ \else
+ \pgfutil@in@,{#1}
+ \ifpgfutil@in@
+ \sseq@parsecoord@maincoord@@#1,\sseq@nil
+ \else
+ \pgfutil@in@{lastclass}{#1}
+ \ifpgfutil@in@
+ \def\handledname{#1}
+ \else
+ \sseq@protectedeval{\@nx\sseq@classname@handler{\sseq@classnameprefix#1\sseq@classnamepostfix}}
+ \let\handledname\result
+ \fi
+ \sseq@obj@ifdef{class.namedclass.\detokenize\@xp{\handledname}}{
+ \let\sourcename\handledname
+ \@xpthree\sseq@parsecoord@maincoord@named\sseq@obj{class.namedclass.\detokenize\@xp{\sourcename}}\sseq@nil
+ }{
+ \sseq@error{invalid-coordinate}\sseq@breakfifi
+ }
+ \fi
+ \fi
+}
+
+\def\sseq@parsecoord@maincoord@@#1,#2,#3\sseq@nil{%
+ \sseq@ifintexpr{#1}{%
+ \sseq@ifintexpr{#2}{}{\sseq@error@n{invalid-coordinate}{y~}\sseq@breakfifi} % breakfifi to get out of ifs from maincoord@
+ \edef\sseq@xcoord{\the\numexpr\sseq@x+(#1)\relax}%
+ \edef\sseq@ycoord{\the\numexpr\sseq@y+(#2)\relax}%
+ \edef\sseq@xycoord{\sseq@xcoord,\sseq@ycoord}%
+ \sseq@ifempty{#3}{\let\sseq@restcoord\pgfutil@empty}{%
+ \edef\sseq@restcoord{\sseq@removecomma#3\sseq@nil}%
+ }%
+ }{%
+ \pgfutil@in@{lastclass}{#1}%
+ \ifpgfutil@in@
+ \def\handledname{#1}%
+ \else
+ \sseq@protectedeval{\@nx\sseq@classname@handler{\sseq@classnameprefix#1\sseq@classnamepostfix}}%
+ \let\handledname\result
+ \fi
+ \sseq@obj@ifdef{class.namedclass.\detokenize\@xp{\handledname}}{%
+ \def\sourcename{#1}
+ \@xpthree\sseq@parsecoord@maincoord@named\sseq@obj{class.namedclass.\detokenize{#1}}\sseq@nil
+ \edef\sseq@restcoord{\sseq@removecomma#2,#3\sseq@nil}%
+ }{%
+ \sseq@error@n{invalid-coordinate}{x~}\sseq@breakfifi % breakfifi to get out of ifs from maincoord@
+ }%
+ }%
+}%
+
+\def\sseq@parsecoord@maincoord@named#1,#2,#3\sseq@nil{
+ \def\sseq@xcoord{#1}%
+ \def\sseq@ycoord{#2}%
+ \edef\sseq@xycoord{\sseq@xcoord,\sseq@ycoord}
+ \def\sseq@restcoord{}
+ \pgfkeys@spdef\sseq@rawindex{#3}
+ \def\sseq@index{#3}
+ \let\rawindex\sseq@rawindex
+ \let\index\sseq@index
+}
+
+\def\sseq@removecomma#1,\sseq@nil{#1}
+
+
+% "Input" to this command is stored in the following commands:
+% \sseq@xycoord -- x,y
+% \sseq@rawindex -- n
+% \sseq@errortype -- description for error messages
+\def\sseq@errortype@tikzprim{tikz ~ primitive}% This is sort of a silly hack, but this first error check shouldn't happen in a tikz primitive.
+\def\sseq@parsecoord@index{%
+ \sseq@obj@ifundef{partcoord.(\sseq@xycoord).numnodes}{
+ \ifsseq@parsecoord@allownonexisting
+ \ifx\sseq@rawindex\pgfutil@empty
+ \def\sseq@index{} % Communicates to \sseq@tikzprimitives@coords@maybeclass that this is not a class
+ \else
+ \sseq@error@x{no-classes}{(\sseq@xycoord)} \sseq@fbreak
+ \fi
+ \else
+ \sseq@error@x{no-classes}{(\sseq@xycoord)} \sseq@fbreak
+ \fi
+ }{
+ \ifx\sseq@rawindex\pgfutil@empty
+ \def\sseq@index{1}
+ \else
+ \sseq@obj@ifdef{partcoord.(\sseq@xycoord).tag.\sseq@class@tagprefix\sseq@rawindex}{% If the raw index is a tag
+ \edef\sseq@index{\sseq@obj{partcoord.(\sseq@xycoord).tag.\sseq@class@tagprefix\sseq@rawindex}}
+ }{%
+ \sseq@ifintexpr{\sseq@rawindex}{%
+ \edef\sseq@rawindexeval{\the\numexpr\sseq@rawindex}
+ \ifnum\sseq@rawindexeval<\z@%
+ \edef\sseq@index{\the\numexpr\sseq@obj{partcoord.(\sseq@xycoord).numnodes} + \sseq@rawindex + \@ne\relax}%
+ \ifnum\sseq@index<\@ne
+ \sseq@error@xxx{index-too-large}{\sseq@rawindex}{\sseq@obj{partcoord.(\sseq@xycoord).numnodes}}{(\sseq@xycoord)}%
+ \edef\sseq@index{1}
+ \sseq@fbreak
+ \fi
+ \else
+ \edef\sseq@index{\the\numexpr\sseq@rawindex}
+ \ifnum\sseq@rawindexeval>\sseq@obj{partcoord.(\sseq@xycoord).numnodes}\relax
+ \sseq@error@xxx{index-too-large}{\sseq@rawindex}{\sseq@obj{partcoord.(\sseq@xycoord).numnodes}}{(\sseq@xycoord)}%
+ \edef\sseq@rawindex{\sseq@obj{partcoord.(\sseq@xycoord).numnodes}}
+ \sseq@fbreak
+ \fi
+ \fi
+ }{
+ \sseq@error@xx{undefined-tag}{\sseq@rawindex}{(\sseq@xycoord)}%
+ \def\sseq@index{1}%
+ }
+ }
+ \fi
+ }
+}
+
+% #1 -- name to define
+% #2 -- coordinate
+% #3 -- the error message
+\def\sseq@eatparens(#1){#1}
+\def\sseq@parsecoord#1#2#3{%
+ \def\sseq@index{}%
+ \def\sseq@errortype{#3}%
+ \sseq@parsecoord@maincoord{#2}%
+ \exp_args:NNf\def\sseq@rawindex{\exp_args:No\sseq@trimspaces{\sseq@restcoord}}
+ \ifx\sseq@index\pgfutil@empty
+ \@xp\sseq@parsecoord@index
+ \fi
+ \sseq@parsecoord@setupvariables{#1}{#2}
+}
+
+\newif\ifsseq@parsecoord@allownonexisting
+\def\sseq@parsecoord@allownonexisting#1#2#3{%
+ \sseq@parsecoord@allownonexistingtrue
+ \sseq@parsecoord{#1}{#2}{#3}
+ \sseq@parsecoord@allownonexistingfalse
+}
+
+
+\def\sseq@parsecoord@setupvariables#1#2{
+ % TODO: clean this up a bit? it's pretty ad-hoc.
+ % Whenever you update this, make sure to update sseq@paredcoords@swap below too.
+ \@xp\let\csname x\sseq@macroname#1\endcsname\sseq@xcoord
+ \@xp\let\csname y\sseq@macroname#1\endcsname\sseq@ycoord
+ \@xp\let\csname n\sseq@macroname#1\endcsname\sseq@index
+ \@xp\let\csname \sseq@macroname#1@partial\endcsname\sseq@xycoord
+ \edef#1{(\sseq@xycoord,\sseq@index)}%
+ \@xp\edef\csname\sseq@macroname#1nopar\endcsname{\sseq@xycoord,\sseq@index}
+ \@xp\edef\csname\sseq@macroname#1name\endcsname{(#2)}%
+ \@xp\edef\csname\sseq@macroname#1nameunbraced\endcsname{\@xptwo\sseq@eatparens\csname\sseq@macroname#1name\endcsname}% edef?
+ \@xp\edef\csname\sseq@macroname#1@internalname\endcsname{sseq{\sseq@xycoord,\sseq@index}}%
+}
+
+
+% This quiets parsecoord for the rest of the current scope.
+% The reason we did it this way is because parsecoord makes too many local definitions...
+\def\sseq@quiet@parsecoord{
+ \msg_redirect_name:nnn {spectralsequences}{invalid-coordinate}{none}
+ \msg_redirect_name:nnn {spectralsequences}{no-classes}{none}
+ \msg_redirect_name:nnn {spectralsequences}{index-too-large}{none}
+ \msg_redirect_name:nnn {spectralsequences}{undefined-tag}{none}
+}
+
+
+% These are used for \replacesource and \replacetarget, though they might be useful for other things too.
+\def\sseq@parsedcoord@save#1#2{
+ \cs_gset_eq:cc { sseq@parsedcoord@save@x#1 } { x\sseq@macroname#2 }
+ \cs_gset_eq:cc { sseq@parsedcoord@save@y#1 } { y\sseq@macroname#2 }
+ \cs_gset_eq:cc { sseq@parsedcoord@save@n#1 } { n\sseq@macroname#2 }
+ \cs_gset_eq:cc { \sseq@macroname#1@partial } { \sseq@macroname#2@partial }
+ \cs_gset_eq:cN { sseq@parsedcoord@save@#1 } #2
+ \cs_gset_eq:cc { sseq@parsedcoord@save@#1nopar } { \sseq@macroname#2nopar }
+ \cs_gset_eq:cc { sseq@parsedcoord@save@#1name } { \sseq@macroname#2name }
+ \cs_gset_eq:cc { sseq@parsedcoord@save@#1nameunbraced } { \sseq@macroname#2nameunbraced }
+ \cs_gset_eq:cc { sseq@parsedcoord@save@#1@internalname } { \sseq@macroname#2@internalname }
+}
+
+\def\sseq@parsedcoord@restore#1#2{
+ \cs_set_eq:cc { x\sseq@macroname#1 } { sseq@parsedcoord@save@x#2 }
+ \cs_set_eq:cc { y\sseq@macroname#1 } { sseq@parsedcoord@save@y#2 }
+ \cs_set_eq:cc { n\sseq@macroname#1 } { sseq@parsedcoord@save@n#2 }
+ \cs_set_eq:cc { \sseq@macroname#1@partial } { #2@partial }
+ \cs_set_eq:Nc #1 { sseq@parsedcoord@save@#2 }
+ \cs_set_eq:cc { \sseq@macroname#1nopar } { sseq@parsedcoord@save@#2nopar }
+ \cs_set_eq:cc { \sseq@macroname#1name } { sseq@parsedcoord@save@#2name }
+ \cs_set_eq:cc { \sseq@macroname#1nameunbraced } { sseq@parsedcoord@save@#2nameunbraced }
+ \cs_set_eq:cc { \sseq@macroname#1@internalname } { sseq@parsedcoord@save@#2@internalname }
+}
+
+% #1 & #2 -- the two base commands that were handed to \sseq@parsecoord to swap.
+% This is only used in \structline to normalize the name of structure lines, so that sseqpages knows what you are talking about
+% if you say \structline(coorda)(coordb) and then later \structline(coordb)(coorda)
+\def\sseq@parsedcoords@swap#1#2{
+ \sseq@parsedcoord@save{swaptempa}{#1}
+ \sseq@parsedcoord@save{swaptempb}{#2}
+ \sseq@parsedcoord@restore{#1}{swaptempb}
+ \sseq@parsedcoord@restore{#2}{swaptempa}
+}
+
+% EXPOSE: a wrapper around \sseq@parsecoord. Use full name of coordinate to avoid name class with sseq@parsecoord.
+\sseq@DeclareDocumentCommand \parsecoordinate { m r() }{%
+ \sseq@parsecoord{#1}{#2}{call of \string\parsecoord}%
+}
+
+% #1 -- coordinate
+\def\sseq@cparsecoord#1{%
+ \def\sseq@index{}
+ \sseq@parsecoord@maincoord{#1}%
+ \ifx\sseq@index\pgfutil@empty\else\sseq@error{named-coordinate-class}\fi% probably not possible to get this error
+ \ifx\sseq@restcoord\pgfutil@empty\else\sseq@error{class-extra-coords}\fi%
+ \let\partialcoord\sseq@xycoord
+ \let\xcoord\sseq@xcoord
+ \let\ycoord\sseq@ycoord
+ \edef\nodenum{\sseq@obj@ifundef{partcoord.(\partialcoord).numnodes}{1}{\the\numexpr\sseq@obj{partcoord.(\partialcoord).numnodes}+1}}%
+ \edef\coordnopar{\xcoord,\ycoord,\nodenum}
+ \edef\coord{(\coordnopar)}%
+}
+
+
+% #1 -- page
+% #2 -- coordinate
+% #3 -- target coordinate or "No Value"
+\def\sseq@dparsecoord#1#2#3{%
+ \def\sseq@index{}%
+ \def\sseq@errortype{differential}%
+ \sseq@parsecoord@maincoord{#2}%
+ \ifx\sseq@index\pgfutil@empty
+ \edef\sseq@rawsindex{\@xp\sseq@dparsecoord@getxindex\@xp{\sseq@restcoord}}%
+ \@xp\pgfkeys@spdef\@xp\sseq@rawsindex\@xp{\sseq@rawsindex}
+ \edef\sseq@rawtindex{\@xp\sseq@dparsecoord@getyindex\@xp{\sseq@restcoord}}%
+ \@xp\pgfkeys@spdef\@xp\sseq@rawtindex\@xp{\sseq@rawtindex}
+%
+ \let\sseq@rawindex\sseq@rawsindex
+ \def\sseq@errortype{source ~ of ~ differential}
+ \@xp\sseq@parsecoord@index % the \@xp is needed so that \sseq@break inside of parsecoordindex works properly.
+ \else
+ \edef\sseq@rawtindex{\@xp\sseq@dparsecoord@getxindex\@xp{\sseq@restcoord}}%
+ \@xp\pgfkeys@spdef\@xp\sseq@rawtindex\@xp{\sseq@rawtindex}
+ \edef\sseq@shouldbeempty{\@xp\sseq@dparsecoord@getyindex\@xp{\sseq@restcoord}}
+ \ifx\sseq@shouldbeempty\pgfutil@empty\else
+ \sseq@error{d-named-coord-two-indexes}
+ \fi
+ \fi
+ \sseq@parsecoord@setupvariables{\source}{(\sseq@xycoord\ifx\sseq@rawindex\pgfutil@empty\else,\sseq@rawindex\fi)}
+ \let\sourcecoord\source@partial
+ \IfNoValueTF{#3}{
+ \ifsseq@hasdegree\else\sseq@error{d-no-degree}\@xp\sseq@break\fi
+ \sseq@eval{\edef\@nx\sseq@xycoord{\@nx\sseq@differential@gettarget{#1}{\sseq@xycoord}}}
+ \let\sseq@rawindex\sseq@rawtindex
+ \def\sseq@errortype{target ~ of ~ differential}
+ \sseq@parsecoord@index
+ \let\targetcoord\sseq@xycoord
+ \sseq@parsecoord@setupvariables{\target}{(\sseq@xycoord\ifx\sseq@rawindex\pgfutil@empty\else,\sseq@rawindex\fi)}
+ }{
+ \ifx\sseq@rawtindex\pgfutil@empty\else
+ \sseq@error{d-target-index-target-coord}
+ \fi
+ \sseq@parsecoord\target{#3}{differential}
+ \let\targetcoord\target@partial
+ \ifsseq@hasdegree
+ \ifsseq@strictdegree
+ \sseq@eval{\edef\@nx\sseq@checktarget{\@nx\sseq@differential@gettarget{#1}{\sourcecoord}}}
+ \ifx\sseq@checktarget\targetcoord\else
+ \sseq@error@xxx{d-wrong-degree}{(\sseq@checktarget)}{\targetname}{(\targetcoord)}
+ \fi
+ \fi
+ \fi
+ }
+ \sseq@parsedcoord@save{lastsource}{\source}
+ \sseq@parsedcoord@save{lasttarget}{\target}
+}
+
+\def\sseq@differential@gettarget#1#2{\sseq@differential@gettarget@{#1}#2\sseq@nil}
+\def\sseq@differential@gettarget@#1#2,#3\sseq@nil{\the\numexpr#2+\sseq@targetx{#1}\relax,\the\numexpr#3+\sseq@targety{#1}\relax}
+
+\def\sseq@getfirsttwoelts#1,#2,#3\sseq@nil{#1,#2}
+\protected\def\sseq@getdtarget#1#2#3{\sseq@eval{\edef\@nx#1{\@nx\sseq@differential@gettarget{#2}{\sseq@getfirsttwoelts#3,,\sseq@nil}}}}
+
+% Takes a comma separated list that can be empty, have one element, or two elements, returns first element (or empty if list is empty).
+\def\sseq@dparsecoord@getxindex#1{\sseq@dparsecoord@getxindex@#1,\sseq@nil}
+\def\sseq@dparsecoord@getxindex@#1,#2\sseq@nil{#1}
+% Takes a comma separated list that can be empty, have one element, or two elements, returns second element (or empty if less than two elements).
+\def\sseq@dparsecoord@getyindex#1{\sseq@dparsecoord@getyindex@#1,\sseq@nil}
+\def\sseq@dparsecoord@getyindex@#1,#2\sseq@nil{%
+ \sseq@ifempty{#2}{}{%
+ \sseq@removecomma#2\sseq@nil%
+ }%
+}
+
+% EXPOSE: a wrapper around \sseq@parsecoord.
+\sseq@DeclareDocumentCommand\parsecoordinate { m r() }{%
+ \sseq@parsecoord{#1}{#2}{call of \string\parsecoord}%
+}
+
+
+% EXPOSE: a wrapper around \sseq@dparsecoord.
+\protected\def\sseq@parsedifferential{%
+ \sseq@d@grabpage\sseq@parsedifferential@
+}
+
+\DeclareDocumentCommand \sseq@parsedifferential@ { d() d() } {%
+ \sseq@eval{\@nx\sseq@dparsecoord{\sseq@dpage}{\IfNoValueTF{#1}{\lastclass0}{\unexpanded{#1}}}{\unexpanded{#2}}}%
+}
+
+% Expandable coordinate parsing for \isalive.
+% Doesn't do any error checking -- the error checking is done by passing it through \sseq@parsecoord in a non-\edef context.
+% #1 -- a callback. Must be a single command b/c it's an N in \exp_args
+% #2 -- the coordinate
+\cs_set:Npn \exp_last_unbraced:NNNf #1#2#3#4
+ {
+ \exp_after:wN #1
+ \exp_after:wN #2
+ \exp_after:wN #3
+ \exp:w \exp_end_continue_f:w #4
+ }
+
+\def\sseq@parsecoordex{\exp_last_unbraced:NNNf\sseq@parsecoordex@}
+\def\sseq@parsecoordex@#1(#2){\sseq@parsecoordex@@#1#2,\@nil}
+
+% Older versions of etex don't provide \expanded so need more expensive version of \exp_args:NNof.
+\ifx\expanded\undefined
+ \cs_set:Npn\exp_args:NNof { \::N \::o \::f \::: }
+\else
+ \cs_set:Npn\exp_args:NNof #1#2#3#4 {
+ \expanded {
+ \exp_not:N #1
+ \exp_not:N #2
+ { \unexpanded \expandafter { #3 } }
+ { \unexpanded \expandafter { \exp:w \exp_end_continue_f:w #4} }
+ }
+ }
+\fi
+
+\def\sseq@parsecoordex@@#1#2,#3,#4\@nil{
+ \ifx&#4&
+ \exp_args:NNo\sseq@parsecoord@indexex#1{\the\numexpr#2\@xp,\the\numexpr#3}{1}
+ \else
+ \exp_args:NNof\sseq@parsecoord@indexex#1{\the\numexpr#2\@xp,\the\numexpr#3\@xp\relax}{\exp_args:Nf\sseq@trimspaces{\sseq@removecomma#4\sseq@nil}}
+ \fi
+}
+
+
+% This is \sseq@parsecoord@index pared down as much as possible -- all error checking is removed because that will happen in a
+% \setbox0=\hbox{stuff} sequestered calculation that uses \sseq@parsecoord@index instead.
+% #1 -- callback which takes one argument -- the final coordinate in parentheses.
+% #2 -- x,y
+% #3 -- n
+\def\sseq@parsecoord@indexex#1#2#3{%
+ \sseq@obj@ifdef{partcoord.(#2).tag.\sseq@class@tagprefix#3}{% If the raw index is a tag
+ \exp_args:NNo\sseq@parsecoord@indexex@#1{\the\numexpr\sseq@obj{partcoord.(#2).tag.\sseq@class@tagprefix#3}}{#2}%
+ }{%
+ \ifnum\numexpr#3<0\space
+ \exp_args:NNo\sseq@parsecoord@indexex@#1{\the\numexpr\sseq@obj{partcoord.(#2).numnodes} + #3 + 1}{#2}%
+ \else
+ \exp_args:NNo\sseq@parsecoord@indexex@#1{\the\numexpr#3}{#2}%
+ \fi
+ }%
+}
+\def\sseq@parsecoord@indexex@#1#2#3{
+ #1(#3,#2)
+}
+
+
+
+
+%%%
+%%% The Stack
+%%%
+
+% This maybe should be moved into sseqmain.code.tex...
+\sseq@addtostorelist\sseq@stacktop{}
+\def\sseq@stackitem#1#2{\csname sseq.\sseq@thename.stack.#1.#2\endcsname} % gobble #2 which we added in for the error message
+\def\sseq@getstackentry#1#2{
+ \ifnum#1<\sseq@stackdepth
+ \exp_args:No\sseq@getstackentry@{\sseq@stacktop}{#1}{#2}
+ \else
+ \sseq@protecterror{\sseq@error@xx{index-out-of-bounds}{#1}{\the\sseq@stackdepth}}
+ \fi
+}
+
+\def\sseq@getstackentry@#1#2{
+ \ifnum#2=\z@\@xp\@firstoftwo\else\@xp\@secondoftwo\fi
+ { \sseq@stackitem{#1} }
+ { \exp_args:Nco\sseq@getstackentry@ { sseq.\sseq@thename.stack.#1.next } { \the\numexpr#2-1 } }
+}
+
+
+\protected\def\sseq@pushstack(#1){
+ \bgroup
+ \sseq@parsecoord@allownonexisting\temp{#1}{}
+ \sseq@pushstack@\temp
+ \sseq@breakpoint
+ \egroup
+}
+
+% Note that \class directly calls pushstack@ to avoid reparsing the coordinate
+\def\sseq@pushstack@{\@xp\sseq@pushstack@@}
+
+\def\sseq@pushstack@@(#1,#2,#3){
+ %\def\temp{#1,#2,#3}\show\temp
+ \global\advance\sseq@stackdepth\@ne
+ \@xp\gdef\csname sseq.\sseq@thename.stack.\the\sseq@stackdepth.lastx\endcsname{#1}
+ \@xp\gdef\csname sseq.\sseq@thename.stack.\the\sseq@stackdepth.lasty\endcsname{#2}
+ \@xp\xdef\csname sseq.\sseq@thename.stack.\the\sseq@stackdepth.lastclass\endcsname{lastclass.\the\sseq@stackdepth}
+ \sseq@obj@gdef{class.namedclass.lastclass.\the\sseq@stackdepth}{#1,#2,#3}
+ \global\@xp\let\csname sseq.\sseq@thename.stack.\the\sseq@stackdepth.next\endcsname\sseq@stacktop
+ \xdef\sseq@stacktop{\the\sseq@stackdepth}
+}
+
+
+\newcount\sseq@userstacksavecount
+\protected\def\sseq@savestack{
+ \global\advance\sseq@userstacksavecount\@ne
+ \sseq@savestack@name{usersave@\the\sseq@userstacksavecount}
+}
+\protected\def\sseq@restorestack{
+ \sseq@restorestack@name{usersave@\the\sseq@userstacksavecount}
+ \global\advance\sseq@userstacksavecount\m@ne
+}
+
+\def\sseq@savestack@name#1{\sseq@obj@xdef{stack.save#1}{\sseq@stacktop}}
+
+\def\sseq@restorestack@name#1{\xdef\sseq@stacktop{\sseq@obj{stack.save#1}}}
+
+\def\sseq@lastx#1{
+ % \romannumeral is testing here whether #1 is a nonnegative integer
+ % if #1 consists of a single nonnegative integer then -0#1 will be turned into some nonpositive integer
+ % and romannumeral produces no output when handed a nonpositive integer, so the result will be empty.
+ % If #1 has any nonnumerical tokens or is negative, there will be left over stuff.
+ %
+ % We also have to subtract off \sseq@x so that this behaves correctly inside of a scope (the \lastx value should be unshifted)
+ \@xp\sseq@ifempty\@xp{\romannumeral-0#1}{
+ \the\numexpr\sseq@getstackentry{#1}{lastx}-\sseq@x\relax
+ }{
+ \the\numexpr\sseq@getstackentry{0}{lastx}-\sseq@x\relax#1 % #1 isn't a number so put it back
+ }
+}
+\def\sseq@lasty#1{
+ \@xp\sseq@ifempty\@xp{\romannumeral-0#1}{
+ \the\numexpr\sseq@getstackentry{#1}{lasty}-\sseq@y\relax
+ }{
+ \the\numexpr\sseq@getstackentry{0}{lasty}-\sseq@y\relax#1 % #1 isn't a number so put it back
+ }
+}
+
+\def\sseq@lastclass#1{
+ \@xp\sseq@ifempty\@xp{\romannumeral-0#1}{
+ \sseq@getstackentry{#1}{lastclass}
+ }{
+ \sseq@getstackentry{0}{lastclass}#1 % #1 isn't a number so put it back
+ }
+}
+
+
+\sseq@DeclareDocumentCommand\nameclass {mr()} {
+ \bgroup
+ \sseq@setthiscall{\sseq@nameclass#1(#2)}
+ \let\sseq@possibleoninputlinepar\empty
+ \sseq@parsecoord\coord{#2}{}
+ \sseq@cleanup@obj{class.namedclass.#1}
+ \sseq@obj@xdef{class.namedclass.#1}{\coordnopar}
+ \sseq@breakpoint
+ \egroup
+}
+
+\sseq@DeclareDocumentCommand\tagclass{mr()}{
+ \bgroup
+ \sseq@setthiscall{\tagclass#1(#2)}
+ \let\sseq@possibleoninputlinepar\empty
+ \def\sseq@class@tag{#1}
+ \sseq@parsecoord\coord{#2}{}%
+ \sseq@obj@ifdef{partcoord.(\sseq@xycoord).tag.\sseq@class@tagprefix\sseq@class@tag}{%
+ \sseq@error@nn{class-tag-already-defined}{\sseq@class@tagprefix\sseq@class@tag}{(\sseq@xycoord)}% could be warning
+ }%
+ \sseq@cleanup@obj{class.\coord.tag}%
+ \sseq@obj@xdef{class.\coord.tag}{\sseq@class@tagprefix\sseq@class@tag}%
+ \sseq@cleanup@obj{partcoord.(\sseq@xycoord).tag.\sseq@class@tagprefix\sseq@class@tag}%
+ \sseq@obj@xdef{partcoord.(\sseq@xycoord).tag.\sseq@class@tagprefix\sseq@class@tag}{\sseq@index}%
+ \sseq@breakpoint
+ \egroup
+}
+
+\sseq@DeclareDocumentCommand\gettag{mr()}{
+ \bgroup
+ \sseq@setthiscall{\gettag#1(#2)}
+ \let\sseq@possibleoninputlinepar\empty
+ \sseq@parsecoord\coord{#2}{}
+ \sseq@obj@ifdef{class.\coord.tag}{
+ \xdef#1{\sseq@obj{class.\coord.tag}}
+ }{
+ \sseq@error@n{class-no-tag}{(#2)}
+ }
+ \sseq@breakpoint
+ \egroup
+}
+
+\prg_new_conditional:Npnn \sseq_if_empty:n #1 {T,F,TF} {
+ \sseq@ifempty{\prg_return_true:}{\prg_return_false:}
+}
+
+\let\SseqIfEmptyTF\sseq_if_empty:nTF
+\let\SseqIfEmptyT\sseq_if_empty:nT
+\let\SseqIfEmptyF\sseq_if_empty:nF
+
+% Test if a class exists.
+\prg_new_conditional:Npnn \sseq_if_exists:n #1 { T , F , TF } {
+ \bgroup
+ \sseq@tempiftrue
+ \sseq@quiet@parsecoord
+ \sseq@parsecoord\coord{#1}{}
+ \@gobbletwo\sseq@breakpoint\sseq@tempiffalse % this executes \sseq@tempiffalse if we broke, otherwise it gets gobbled
+ \ifsseq@tempif
+ \egroup
+ \prg_return_true:
+ \else
+ \egroup
+ \prg_return_false:
+ \fi
+}
+
+% Test if a class is out of bounds. Return false if the class doesn't exist.
+\prg_new_conditional:Npnn \sseq_if_out_of_bounds:n #1 { T , F , TF } {
+ \bgroup
+ \sseq@tempiftrue
+ \sseq@quiet@parsecoord
+ \sseq@parsecoord\coord{#1}{}
+ \@gobbletwo\sseq@breakpoint\sseq@tempiffalse % this executes \sseq@tempiffalse if we broke, otherwise it gets gobbled
+ \ifsseq@tempif
+ \sseq_if_out_of_bounds_inner:nnTF { \xcoord } { \ycoord }{\egroup\prg_return_true:}{\egroup\prg_return_false:}
+ \else
+ \egroup
+ \prg_return_false:
+ \fi
+}
+
+% For internal use b/c it's faster not to call \sseq@parsecoord
+% Make sure to add \sseq@x and \sseq@y to the coordinate before passing along
+% to if_out_of_bounds_inner to correctly account for translations.
+\prg_new_conditional:Npnn \sseq_if_out_of_bounds_noparse:nn #1#2 {TF} {
+ \sseq_if_out_of_bounds_inner:nnTF { #1 + \sseq@x } { #2 + \sseq@y }
+ { \prg_return_true: }
+ { \prg_return_false: }
+}
+
+
+\prg_new_conditional:Npnn \sseq_if_out_of_bounds_inner:nn #1#2 {TF} {
+ \bool_lazy_all:nTF {
+ { \int_compare_p:nNn { #1 } < { \sseq@xmaxpp } }
+ { \int_compare_p:nNn { #1 } > { \sseq@xminmm } }
+ { \int_compare_p:nNn { #2 } < { \sseq@ymaxpp } }
+ { \int_compare_p:nNn { #2 } > { \sseq@yminmm } }
+ }
+ { \prg_return_false: }
+ { \prg_return_true: }
+}
+
+\prg_new_conditional:Npnn \sseq_if_alive:nn #1 #2 { T, F, TF } {
+ \bgroup
+ \sseq@tempiftrue
+ \sseq@quiet@parsecoord
+ \sseq@parsecoord\coord{#2}{}
+ \@gobbletwo\sseq@breakpoint\sseq@tempiffalse
+ \ifsseq@tempif
+ \sseq@obj@pagetogen{class.\coord}{#1}
+ \ifnum\sseq@gen>-1\relax
+ \egroup
+ \prg_return_true:
+ \else
+ \egroup
+ \prg_return_false:
+ \fi
+ \else
+ \egroup
+ \prg_return_false:
+ \fi
+}
+
+
+\protected\def\sseq@DrawIfValidDifferential{\sseq@DrawIfValidDifferential@{}{}}
+\protected\def\sseq@DrawIfValidDifferentialT{\sseq@DrawIfValidDifferential@{T}{}}
+\protected\def\sseq@DrawIfValidDifferentialF{\sseq@DrawIfValidDifferential@{}{F}}
+\protected\def\sseq@DrawIfValidDifferentialTF{\sseq@DrawIfValidDifferential@{T}{F}}
+
+\DeclareDocumentCommand\sseq@DrawIfValidDifferential@{mmO{}}{%
+ \begingroup
+ \sseq@loadinputline
+ \def\sseq@dtype{d}
+ \def\sseq@trueclause{#1}
+ \def\sseq@falseclause{#2}
+ \def\sseq@doptions{#3}
+ \sseq@d@grabpage\sseq@DrawIfValidDifferential@@
+}
+
+
+% #4 -- first coord
+% #5 -- possible second coord
+\DeclareDocumentCommand \sseq@DrawIfValidDifferential@@ { d() d() } {%
+ \ifx\sseq@trueclause\pgfutil@empty
+ \@xp\@firstoftwo
+ \else
+ \@xp\@secondoftwo
+ \fi{\sseq@DrawIfValidDifferential@@@{#1}{#2}{}}
+ {\sseq@DrawIfValidDifferential@@@{#1}{#2}}
+}
+
+% #1 -- first coord
+% #2 -- possible second coord
+% #3 -- rest of true clause
+\def \sseq@DrawIfValidDifferential@@@#1#2#3{
+ \sseq@eval{\endgroup%\@nx\tracingall
+ \exp_not:c{sseq_if_valid_differential:nnT\sseq@falseclause}{\sseq@dpage}{\IfNoValueTF{#1}{\lastclass0}{\unexpanded{#1}}}{\unexpanded{#2}}{%
+ \@nx\d[\exp_not:o{\sseq@doptions}]\sseq@dpage\IfNoValueF{#1}{(\unexpanded{#1})}\IfNoValueF{#2}{(\unexpanded{#2})}%
+ \unexpanded{#3}%
+ }%
+ }%
+}
+
+\protected\def\sseq@IfValidDifferentialTF{\sseq@IfValidDifferential{TF}}
+\protected\def\sseq@IfValidDifferentialT{\sseq@IfValidDifferential{T}}
+\protected\def\sseq@IfValidDifferentialF{\sseq@IfValidDifferential{F}}
+
+\protected\def\sseq@IfValidDifferential#1{%
+ \bgroup
+ \def\sseq@truefalseclauses{#1}
+ \sseq@d@grabpage{\sseq@IfValidDifferential@}
+}
+
+\DeclareDocumentCommand \sseq@IfValidDifferential@ { d() d() } {%
+ \sseq@eval{\egroup\exp_not:c{sseq_if_valid_differential:nn\sseq@truefalseclauses}{\sseq@dpage}{\IfNoValueTF{#1}{\lastclass0}{\unexpanded{#1}}}{\unexpanded{#2}}}%
+}
+
+\prg_new_conditional:Npnn \sseq_if_valid_differential:nn #1 #2 #3 { T, F, TF } {
+ \bgroup
+ \sseq@tempiftrue
+ \sseq@ifintexpr{#1}{%
+ \edef\sseq@pagenum{\the\numexpr#1\relax}
+ }{%
+ \sseq@break% return false
+ }%
+ \sseq@quiet@parsecoord
+ \msg_redirect_name:nnn {spectralsequences} {d-wrong-degree} {none}
+ \msg_redirect_name:nnn {spectralsequences} {d-target-index-target-coord} {none}
+ \msg_redirect_name:nnn {spectralsequences} {d-named-coord-two-indexes} {none}
+ \msg_redirect_name:nnn {spectralsequences} {d-hit-wrong-order} {none}
+ \msg_redirect_name:nnn {spectralsequences} {d-class-already-hit} {none}
+ \msg_redirect_name:nnn {spectralsequences} {d-hit-same-page-replaceclass}{none}
+ \sseq@dparsecoord{\sseq@pagenum}{#2}{#3}
+ \let\sseq@obj@xdef\@gobbletwo
+ \sseq@d@setpageclass{class.\source}{\sseq@pagenum}{source}
+ \sseq@d@setpageclass{class.\target}{\sseq@pagenum}{target}
+ \@gobbletwo\sseq@breakpoint\sseq@tempiffalse
+ \ifsseq@tempif
+ \egroup
+ \prg_return_true:
+ \else
+ \egroup
+ \prg_return_false:
+ \fi
+}
+
+
+
+\sseq@DeclareDocumentCommand \IfExistsTF { r() } { \sseq_if_exists:nTF { #1 } }
+\sseq@DeclareDocumentCommand \IfExistsT { r() } { \sseq_if_exists:nT { #1 } }
+\sseq@DeclareDocumentCommand \IfExistsF { r() } { \sseq_if_exists:nF { #1 } }
+
+\sseq@DeclareDocumentCommand \IfOutOfBoundsTF { r() } { \sseq_if_out_of_bounds:nTF { #1 } }
+\sseq@DeclareDocumentCommand \IfOutOfBoundsT { r() } { \sseq_if_out_of_bounds:nT { #1 } }
+\sseq@DeclareDocumentCommand \IfOutOfBoundsF { r() } { \sseq_if_out_of_bounds:nF { #1 } }
+\sseq@DeclareDocumentCommand \IfInBoundsTF { r() mm } { \sseq_if_out_of_bounds:nTF { #1 } { #3 } { #2 } }
+\sseq@DeclareDocumentCommand \IfInBoundsT { r() } { \sseq_if_out_of_bounds:nF { #1 } }
+\sseq@DeclareDocumentCommand \IfInBoundsF { r() } { \sseq_if_out_of_bounds:nT { #1 } }
+
+\sseq@DeclareDocumentCommand \IfAliveTF { mr() }{ \sseq_if_alive:nnTF{#1}{#2}}
+\sseq@DeclareDocumentCommand \IfAliveT { mr() } { \sseq_if_alive:nnT{#1}{#2}}
+\sseq@DeclareDocumentCommand \IfAliveF { mr() } { \sseq_if_alive:nnF{#1}{#2}}
+
+
+%%%%%%
+%%%
+%%% Tikz Primitives
+%%%
+%%%%%%
+
+% Replace a tikz command with a command that saves the command on savedpaths
+
+\def\sseq@modtikzcommands{%
+ \let\scope\sseq@scope
+ \let\endscope\sseq@endscope
+ \@xp\sseq@modtikzcommands@\sseq@tikzcommands\sseq@nil
+}
+\def\sseq@modtikzcommands@#1{%
+ \ifx#1\sseq@nil\else
+ \protected\edef#1{\@xp\@nx\csname\sseq@macroname#1\space\endcsname\@nx#1}%
+ \@xp\let\csname\sseq@macroname#1\space\endcsname\sseq@defer@tikzcommand % This is just for error reporting so that it will say "Paragraph ended before \draw was complete"
+ \@xp\sseq@modtikzcommands@
+ \fi
+}
+
+\def\sseq@defer@tikzcommand#1{%
+ \begingroup
+ \let\sseq@isaliveprotect\sseq@isaliveprotect@protect
+ \sseq@setinputline
+ \sseq@loadinputline
+ \sseq@callas{#1}%
+ \gdef\sseq@thepathsofar{#1[/utils/exec={\sseq@thesseqstyle\sseq@thetikzprimitivestyle\the\sseq@scope@toks\sseq@savedoptioncode}]}%
+ \xdef\sseq@whattheusersaid{\string#1}
+ \sseq@defer@tikzcommand@
+}
+
+\def\sseq@defer@tikzcommand@{%
+ \futurelet\testtok\sseq@defer@tikzcommand@@
+}
+
+% This serves as a marker to break out of parser.
+% TODO: support \pgfextra? Should we pass \pgfextra along to be handled by tikz parser?
+\def\sseq@defer@tikzcommand@escape{sseq@defer@tikzcommand@escape}
+\def\sseq@defer@tikzcommand@@{%
+ \ifx\testtok;
+ \let\next\sseq@defer@tikzcommand@finish
+ \else
+ \ifx\testtok[%
+ \let\next\sseq@defer@tikzcommand@option
+ \else
+ \ifx\testtok(%
+ \let\next\sseq@defer@tikzcommand@coord
+ \else
+ \@xp\ifx\space\testtok
+ \let\next\sseq@defer@tikzcommand@space
+ \else
+ % TODO: also support "foreach" keyword without the backslash.
+ \ifx\testtok\foreach
+ \let\next\sseq@defer@tikzcommand@foreach
+ \else
+ \ifx\testtok\sseq@defer@tikzcommand@escape % escape is like \pgfextra, breaks out of the parsing.
+ \let\next\@gobble
+ \else
+ \ifx\testtok\bgroup
+ \let\next\sseq@defer@tikzcommand@group
+ \else
+ \let\next\sseq@defer@tikzcommand@other
+ \fi
+ \fi
+ \fi
+ \fi
+ \fi
+ \fi
+ \fi
+ \sseq@call{\next}%
+}
+
+
+\def\sseq@defer@tikzcommand@foreach{
+ \def\pgffor@beginhook{\expandafter\sseq@defer@tikzcommand@\pgfutil@firstofone}
+ \def\pgffor@endhook{\sseq@defer@tikzcommand@escape}
+ \def\pgffor@afterhook{\sseq@defer@tikzcommand@}
+}
+
+\def\sseq@defer@tikzcommand@option[#1]{%
+ \sseq@processoptions{tikz ~ primitives}{#1}%
+ \ifx\sseq@savedoptioncode\pgfutil@empty\else
+ \sseq@x@addto@macro\sseq@thepathsofar{[/utils/exec={\unexpanded\@xp{\@xp\sseq@options@bothpassmode\sseq@savedoptioncode}}]}%
+ \fi
+ \sseq@g@addto@macro\sseq@whattheusersaid{[#1]}
+ \sseq@defer@tikzcommand@
+}
+
+\def\sseq@defer@tikzcommand@coord(#1){
+ \sseq@tikzprimitives@coord(#1)
+ \sseq@g@addto@macro\sseq@whattheusersaid{(#1)}
+ \sseq@defer@tikzcommand@
+}
+
+% When do groups occur in tikz commands? should we try to parse the inside?
+\def\sseq@defer@tikzcommand@group#1{
+ \sseq@g@addto@macro\sseq@whattheusersaid{{#1}}
+ \sseq@xprotected@addto@macro\sseq@thepathsofar{{#1}}%
+ \sseq@defer@tikzcommand@
+}
+
+\long\def\sseq@defer@tikzcommand@other#1{
+ \ifcat$\@xp\@gobble\string#1$ % test for control word. Won't catch active characters.
+ \@xp\use_i:nnn
+ \else
+ \@xp\ifx\csname sseq@illegalintikz@\string#1\endcsname\relax
+ \exp_last_unbraced:Nf \use_ii:nnn
+ \else
+ \exp_last_unbraced:Nf \use_iii:nnn
+ \fi
+ \fi{ % case i: it's not a control word. Just add it to the saved path
+ \sseq@g@addto@macro\sseq@whattheusersaid{#1}
+ \sseq@g@addto@macro\sseq@thepathsofar{#1}%
+ \sseq@defer@tikzcommand@
+ }{ % case ii: it's some different control word. Let's try full expanding it.
+ % note that here we cannot figure out what the user actually said, so we just have to report the f-expanded version.
+ \def\tempa{#1} % save the current value
+ \exp_last_unbraced:Nf \sseq@defer@tikzcommand@other@cs#1
+ }{ % case iii: it's an illegal control word (e.g., \class, \d, \begin, \end, ...) throw an error.
+ \sseq@error@xx{incomplete-tikz}{\unexpanded\@xp{\sseq@whattheusersaid}}{
+ \ifx\par#1 the ~ start ~ of ~ a ~ new ~ paragraph
+ \else \string#1\fi
+ }
+ \sseq@defer@tikzcommand@finish@ % finish@ doesn't try to grab a semicolon, which is good because there isn't one.
+ #1 % Reinsert the token that is causing us to stop
+ }
+}
+
+\def\sseq@defer@tikzcommand@other@cs#1{ % so we f expanded the control sequence, now we test if that did anything
+ \def\tempb{#1} % get the first f expanded token
+ \ifx\tempa\tempb
+ \@xp\@firstoftwo
+ \else
+ \@xp\@secondoftwo
+ \fi{% it didn't get expanded any more, so just add it to the stored stuff
+ \sseq@g@addto@macro\sseq@whattheusersaid{#1}
+ \sseq@g@addto@macro\sseq@thepathsofar{#1}
+ \sseq@defer@tikzcommand@
+ }{% it got expanded. Run it through the main loop again.
+ \sseq@defer@tikzcommand@#1
+ }
+}
+
+% Illegal control words:
+\long\def\sseq@setillegalcontrolwords#1{\sseq@setillegalcontrolwords@#1\sseq@nil}
+\long\def\sseq@setillegalcontrolwords@#1{
+ \ifx#1\sseq@nil\else
+ \@xp\def\csname sseq@illegalintikz@\string#1\endcsname{}
+ \@xp\sseq@setillegalcontrolwords@
+ \fi
+}
+
+% We missing anything here?
+\sseq@setillegalcontrolwords{
+ \class\classoptions\replaceclass\d\doptions\structline\structlineoptions\circleclasses
+ \savestack\restorestack\pushstack\nameclass\foreach\begin\end\par
+ \clip\coordinate\draw\fill\filldraw
+ \graph\matrix\node\path\pattern
+ \shade\shadedraw\useasboundingbox
+}
+
+
+\@xp\def\@xp\sseq@defer@tikzcommand@space\space{%
+ \sseq@g@addto@macro\sseq@thepathsofar{~}%
+ \sseq@g@addto@macro\sseq@whattheusersaid{~}
+ \sseq@defer@tikzcommand@
+}
+
+\def\sseq@defer@tikzcommand@finish;{
+ \sseq@g@addto@macro\sseq@whattheusersaid{;}
+ \sseq@defer@tikzcommand@finish@
+}
+
+% If the expression was incomplete, we'll jump here to avoid adding the semicolon to the error printout
+\def\sseq@defer@tikzcommand@finish@{
+ \sseq@g@addto@macro\sseq@thepathsofar{;}
+ %\show\sseq@thepathsofar
+ \global\sseq@thiscalltoks\@xp{\sseq@whattheusersaid}
+ \ifx\sseq@pageconstraint\sseq@pageconstraint@true
+ %\show\sseq@thepathsofar
+ \sseq@savedpaths@xaddtikzpath{\unexpanded\@xp{\sseq@thepathsofar}}%
+ \else
+ \sseq@savedpaths@xaddtikzpath{%
+ \@nx\sseq@tikzcommand@conditionaldraw{\unexpanded\@xp{\sseq@thepathsofar}}{\unexpanded\@xp{\sseq@pageconstraint}}
+ }%
+ \fi
+ \endgroup
+}
+
+
+\def\sseq@tikzcommand@conditionaldraw#1#2{%
+ \sseq@pgfmathparse@rescan{#2}%
+ \ifnum\pgfmathresult>\z@ #1 \fi
+}
+
+\def\sseq@pgfmathparse@rescan#1{\makeatletter\catcode`\&=12\relax\scantokens{\pgfmathparse{#1}}}
+
+%%% Coordinate parser, copied with huge simplifications from \tikz@@scan@@no@calculator, tikz.code.tex line 4994.
+%%% We don't need to handle any of the weird cases, we just need to know about them so we can give up and let tikz do the work later.
+% TODO: do same coordinate fixing for coordinates in calculations (yeah right! not even clear this is a good idea...)
+\def\sseq@tikzprimitives@coord(#1){
+ \begingroup
+ \let\next\sseq@tikzprimitives@coords@maybenamedclass
+ \ifsseq@tikzprims@integershift
+ \pgfutil@in@${#1}
+ \ifpgfutil@in@
+ \let\next\sseq@tikzprimitives@coords@handlemath
+ \else
+ \pgfutil@in@ :{#1}
+ \ifpgfutil@in@\else
+ \pgfutil@in@{intersection }{#1}
+ \ifpgfutil@in@\else
+ \pgfutil@in@|{#1}%
+ \ifpgfutil@in@\else
+ \pgfutil@in@,{#1}
+ \ifpgfutil@in@
+ \let\next\sseq@tikzprimitives@coords@maybeclass
+ \fi
+ \fi
+ \fi
+ \fi
+ \fi
+ \fi
+ \next{#1}
+ \endgroup
+}
+\def\sseq@tikzprimitives@coords@maybeclass#1{
+ \let\next\sseq@tikzprimitives@coords@notaclass % most branches do this.
+ % First check whether there are any of the protect "to be determined later" variables like \xmax, etc. If there are, it's not a class
+ \sseq@protected@edef\sseq@temp@i{#1}
+ \edef\sseq@temp@ii{#1}
+ \ifx\sseq@temp@i\sseq@temp@ii
+ \@xp\sseq@tikzprimitive@getcoord@anchor#1.\sseq@nil % puts coord into \sseq@tempcoord, anchor if any into \sseq@tempanchor
+ \exp_args:NNo\pgfutil@in@.{\sseq@tempanchornopt} % If there's an extra . in the "anchor", it's not an anchor, and this is not a class
+ \ifpgfutil@in@\else
+ \exp_args:NNo \pgfutil@in@,{\sseq@tempcoord} % If the . we used to delineate the "anchor" was in the x-coordinate, it's not an anchor and this is not a class
+ \ifpgfutil@in@
+ \exp_args:NNo \pgfutil@in@,{\sseq@tempanchornopt} % If there is a comma in the "anchor" it's not an anchor and this is not a class
+ \ifpgfutil@in@\else
+ \ifx\sseq@tempanchor\pgfutil@empty
+ \sseq@tempiftrue
+ \else % If there's an "anchor" we need to test whether the expression is a valid decimal coordinate. If it is, we're going to treat it as not a class
+ % outputs into sseq@tempif, sets sseq@tempiffalse if it IS valid math, b/c then we're not a anchor
+ \sseq@anchortrue % If this ends up getting handled as a tikz coordinate, we'll have to add an "anchor ignored" error
+ \sseq@tikzprimitives@testlastcoord@validmathexpression#1,\sseq@nil
+ \fi
+ \ifsseq@tempif
+ \@xp\sseq@tikzprimitives@ifintcoords\@xp{\sseq@tempcoord}{ % check we're all integer coordinates
+ \sseq@parsecoord@allownonexisting\coord{\sseq@tempcoord}{tikz ~ primitive}%
+ \ifx\sseq@index\pgfutil@empty\else
+ \sseq@x@addto@macro\sseq@thepathsofar{(sseq{\sseq@removeparens\coord}\sseq@tempanchor)}% Okay, we're all set, it's a class
+ \let\next\@gobble % don't run \sseq@tikzprimitives@notaclass
+ \fi
+ }{}%
+ \fi
+ \fi
+ \fi
+ \fi
+ \fi
+ \next{#1}
+}
+
+% Sets tempiffalse if if the second coordinate IS a valid math expression -- then t
+\def\sseq@tikzprimitives@testlastcoord@validmathexpression#1,#2,#3\sseq@nil{
+ \sseq@ifpgfmathexpr{#2}{
+ \sseq@tempiffalse
+ }{
+ \sseq@tempiftrue
+ }
+}
+
+\def\sseq@tikzprimitives@coords@maybenamedclass#1{%
+ \sseq@tikzprimitive@getcoord@anchor#1.\sseq@nil % puts coord into \sseq@tempcoord, anchor if any into \sseq@tempanchor
+ \protected@edef\sseq@tempcoord{\sseq@tempcoord}%
+ % need this detokenize here to prevent it from throwing errors when there's a command inside...
+ \sseq@obj@ifdef{class.namedclass.\detokenize\@xp{\sseq@tempcoord}}{
+ \sseq@x@addto@macro\sseq@thepathsofar{(sseq{\sseq@obj{class.namedclass.\detokenize\@xp{\sseq@tempcoord}}\sseq@tempanchor})}%
+ }{%
+ \sseq@tikzprimitives@coords@notaclass{#1}%
+ }%
+}
+
+\let\sseq@tikzprimitives@coords@maybeclass@save\sseq@tikzprimitives@coords@maybeclass
+
+
+\def\sseq@tikzprimitives@coords@notaclass#1{
+ \def\next{\sseq@tikzprimitives@coords@notaclass@leavetotikz{#1}}
+ \pgfutil@in@${#1}
+ \ifpgfutil@in@\else
+ \pgfutil@in@ :{#1}
+ \ifpgfutil@in@\else
+ \pgfutil@in@{intersection }{#1}
+ \ifpgfutil@in@\else
+ \pgfutil@in@|{#1}%
+ \ifpgfutil@in@\else
+ \pgfutil@in@,{#1}
+ \ifpgfutil@in@
+ \def\next{\sseq@tikzprimitives@coords@notaclass@handle{#1}}
+ \fi
+ \fi
+ \fi
+ \fi
+ \fi
+ \next
+}
+
+\def\sseq@tikzprimitives@coords@notaclass@leavetotikz#1{
+ \sseq@g@addto@macro\sseq@thepathsofar{(#1)}
+}
+
+\def\sseq@tikzprimitives@coords@notaclass@handle#1{
+ \ifsseq@anchor
+ % Still seems impossible to trigger this?
+ \sseq@error@xxx{anchor-ignored}{(#1)}{(\unexpanded\@xp{\sseq@tempcoord})}{\sseq@tempanchor}
+ \@xp\sseq@tikzprimitives@coords@notaclass@handle@\sseq@tempcoord,\sseq@nil
+ \else
+ \sseq@tikzprimitives@coords@notaclass@handle@#1,\sseq@nil
+ \fi
+}
+
+\def\sseq@tikzprimitives@coords@notaclass@handle@#1,#2,#3\sseq@nil{
+ \sseq@ifempty{#3}{
+ \let\sseq@index\pgfutil@empty
+ }{
+ \edef\sseq@index{,\unexpanded\@xp{\sseq@removecomma#3\sseq@nil}}
+ \edef\sseq@indexnocomma{\unexpanded\@xp{\sseq@removecomma#3\sseq@nil}}
+ }
+ \sseq@ifpgfmathexpr{#1}{%
+ % Decide whether we can keep the result, which is stored in \sseq@mathresult
+ \protected@edef\sseq@tempa{#1}
+ \edef\sseq@tempb{#1}
+ \ifx\sseq@tempa\sseq@tempb
+ \let\sseq@tempx\sseq@mathresult
+ \else
+ \def\sseq@tempx{#1}
+ \fi
+ }{
+ \sseq@error@xx{invalid-tikz-coord}{(#1,#2\sseq@index)}{x}
+ \def\sseq@defer@tikzcommand@finish;{\endgroup}
+ }
+ \sseq@ifpgfmathexpr{#2}{
+ % Decide whether we can keep the result, which is stored in \sseq@mathresult
+ \protected@edef\sseq@tempa{#2}
+ \edef\sseq@tempb{#2}
+ \ifx\sseq@tempa\sseq@tempb
+ \let\sseq@tempy\sseq@mathresult
+ \else
+ \def\sseq@tempy{#2}
+ \fi
+ }{
+ \sseq@error@xx{invalid-tikz-coord}{(#1,#2\sseq@index)}{y}
+ \def\sseq@defer@tikzcommand@finish;{\endgroup}
+ }
+ \def\sseq@tempa{#1}
+ \ifx\sseq@index\pgfutil@empty\else
+ \sseq@error@xxx{index-ignored}{(\unexpanded{#1,#2}\unexpanded\@xp{\sseq@index})}{\unexpanded{(#1,#2)}}{\unexpanded\@xp{\sseq@indexnocomma}}
+ \fi
+ \sseq@xprotected@addto@macro\sseq@thepathsofar{(\sseq@tempx,\sseq@tempy)}
+}
+
+\def\sseq@tikzprimitives@ifintcoords#1{\sseq@tikzprimitives@ifintcoords@#1,\sseq@nil}
+\def\sseq@tikzprimitives@ifintcoords@#1,#2,#3\sseq@nil{%
+ \sseq@ifintexpr{#1}{%
+ \sseq@ifintexpr{#2}{%
+ \@firstoftwo
+ }{\@secondoftwo}%
+ }{\@secondoftwo}%
+}
+\def\sseq@tikzprimitives@coords@handlemath#1{\sseq@tikzprimitives@coords@handlemath@(#1)}
+\def\sseq@tikzprimitives@coords@handlemath@#1$#2$){\sseq@g@addto@macro\sseq@thepathsofar{#1$#2$)}}
+\def\sseq@tikzprimitive@getcoord@anchor#1.#2\sseq@nil{
+ \def\sseq@tempcoord{#1}
+ \ifx\sseq@nil#2\sseq@nil
+ \def\sseq@tempanchor{}
+ \def\sseq@tempanchornopt{}
+ \else
+ \edef\sseq@tempanchor{.\sseq@tikzprimitive@getcoord@anchor@eatdot#2}
+ \edef\sseq@tempanchornopt{\sseq@tikzprimitive@getcoord@anchor@eatdot#2}
+ \fi
+}
+\def\sseq@tikzprimitive@getcoord@anchor@eatdot#1.{#1}
+\def\sseq@uptopt#1.#2\sseq@nil{#1}
+%%% Page constraint and \isalive
+% #1 -- the new page constraint
+% #2 -- a binary logical operator (&& or ||).
+\def\sseq@updatepageconstraint#1#2{
+ \def\sseq@isaliveprotect{}
+ \let\sseq@isalive@\sseq@isalive@active
+ \bgroup
+ % Was pretty hard to get \isalive to report errors correctly. And to work in general >_<
+ \let\sseq@isalive@parens\sseq@isalive@parens@check
+ \sseq@d@addto@macro\sseq@error@hook{\let\protect\relax}%
+ \let\protect\@unexpandable@protect
+ \edef\sseq@error@annotation{\unexpanded{.^^J(In page constraint "#1")}\unexpanded\@xp{\sseq@error@annotation}}
+ \selectfont\sseq@restorefont
+ \setbox0=\hbox{#1}
+ \egroup
+ \sseq@protected@edef\sseq@pageconstraint{(\unexpanded\@xp{\sseq@pageconstraint})#2(#1)}
+}
+
+
+\def\sseq@updatepageconstraintrange{%
+ \sseq@protected@edef\sseq@pageconstraint{%
+ (\unexpanded\@xp{\sseq@pageconstraint})&&(\temp<=\page \ifx\tempmax\empty\else&&\page<=\tempmax\fi)%
+ }%
+}
+
+% This protect variant allows us to delay evaluation of \isalive, just so that we can
+% capture something close to the original argument to (does this matter anymore?)
+\let\sseq@isaliveprotect\relax
+\def\sseq@isaliveprotect@protect{\@nx\sseq@isaliveprotect\@nx}
+\def\sseq@isalive{\sseq@isaliveprotect\sseq@isalive@}
+\def\sseq@isalive@{\sseq@isalive@error}
+\def\sseq@isalive@error{\sseq@error{is-alive-illegal-here}}
+\def\sseq@isalive@active#1{%
+ \@xp\ifx\@xp$\@gobble#1$%
+ \@xp\sseq@isalive@parens\@xp#1%
+ \else
+ \sseq@isalive@list#1\sseq@nil
+ \fi
+}
+\def\sseq@isalive@list(#1)#2{%
+ \sseq@isalive@parens(#1)%
+ \ifx\sseq@nil#2\else
+ &&\@xp\sseq@isalive@list\@xp#2%
+ \fi
+}
+
+\def\sseq@isalive@parens@check(#1){
+ \sseq@parsecoord\coord{#1}{\string\isalive}
+}
+\def\sseq@isalive@parens(#1){\sseq@parsecoordex\sseq@isalive@parens@(#1)}
+\def\sseq@isalive@parens@(#1,#2,#3){\@nx\sseq@isalive@final{#1}{#2}{#3}{\sseq@obj{class.(#1,#2,#3).num}}}
+\def\sseq@isalive@final#1#2#3#4{%
+ \ifnum\sseq@obj{class.(#1,#2,#3)[#4].page}<\sseq@thepagecount
+ 0%
+ \else
+ \ifnum#4=\z@
+ 1%
+ \else
+ \ifnum\sseq@obj{class.(#1,#2,#3)[\the\numexpr #4-1\relax].page}=\sseq@thepagecount
+ 0%
+ \else
+ 1%
+ \fi
+ \fi
+ \fi
+}
+
+
+
+
+
+%%%
+%%% This is a macro for typesetting monomials.
+%%%
+
+% We need to check for math subscript characters
+\char_set_catcode_math_subscript:N \_
+
+\def\sseq@support@std{}
+\protected\def\SseqNormalizeMonomialSetVariables#1{%
+ \bgroup
+ \gdef\sseq@support@std{}%
+ \def\sseqnormalizemonomial@add{%
+ \def\temp{1}%
+ \ifx\sseq@var\temp\else
+ \edef\sseq@var{\unexpanded\@xp{\sseq@var}\unexpanded\@xp{\sseq@subscript}}%
+ \@ifundefined{\sseqnormalizemonomial@varcs}{%
+ \@xp\xdef\csname\sseqnormalizemonomial@varcs\endcsname{0}%
+ \sseq@x@addto@macro\sseq@support@std{\@nx\\{\unexpanded\@xp{\sseq@var}}{\sseqnormalizemonomial@varcs}}%
+ }{}%
+ \fi
+ \sseqnormalizemonomial@testend
+ }%
+ \SseqNormalizeMonomial{#1}%
+ \egroup
+}
+
+
+\protected\def\SseqNormalizeMonomial#1{%
+ \bgroup
+ \def\sseq@ourinput{#1}%
+ \let\sseq@support\sseq@support@std
+ \sseq@ifempty{#1}{\egroup\def\result{1}}{%
+ \sseqnormalizemonomial@#1\sseq@nil
+ }%
+}
+\def\sseqnormalizemonomial@#1{%
+ \pgfkeys@spdef\sseq@var{#1}%
+ \def\sseq@power{1}%
+ \def\sseq@subscript{}%
+ \futurelet\testtok\sseqnormalizemonomial@@
+}
+
+\def\sseqnormalizemonomial@@{%
+ \ifx\testtok^%
+ \let\next\sseqnormalizemonomial@sup
+ \else
+ \ifx\testtok_%
+ \let\next\sseqnormalizemonomial@sub
+ \else
+ \let\next\sseqnormalizemonomial@add
+ \fi
+ \fi
+ \next
+}
+
+\def\sseqnormalizemonomial@sup^#1{%
+ \sseq@ifintexpr{#1}{%
+ \edef\sseq@power{\the\numexpr#1\relax}%
+ }{%
+ \sseq@error@xxx{NormalizeMonomial-invalid-exponent}{\unexpanded{#1}}{\unexpanded\@xp{\sseq@var}}{\unexpanded\@xp{\sseq@ourinput}}
+ \let\result\sseq@ourinput
+ \sseq@gobble@to@nil
+ }
+ \futurelet\testtok\sseqnormalizemonomial@@
+}
+
+\def\sseqnormalizemonomial@sub_#1{%
+ \def\sseq@subscript{_{#1}}%
+ \futurelet\testtok\sseqnormalizemonomial@@
+}
+
+\def\sseqnormalizemonomial@add{%
+ \def\temp{1}%
+ \ifx\sseq@var\temp\else
+ \edef\sseq@var{\unexpanded\@xp{\sseq@var}\unexpanded\@xp{\sseq@subscript}}%
+ \@ifundefined{\sseqnormalizemonomial@varcs}{%
+ \@xp\edef\csname\sseqnormalizemonomial@varcs\endcsname{\sseq@power}%
+ \sseq@e@addto@macro\sseq@support{\@nx\\{\unexpanded\@xp{\sseq@var}}{\sseqnormalizemonomial@varcs}}%
+ }{%
+ \@xp\edef\csname\sseqnormalizemonomial@varcs\endcsname{\the\numexpr\csname\sseqnormalizemonomial@varcs\endcsname + \sseq@power}%
+ }%
+ \fi
+ \sseqnormalizemonomial@testend
+}
+
+
+\def\sseqnormalizemonomial@testend{%
+ \ifx\testtok\sseq@nil
+ \sseqnormalizemonomial@done
+ \sseq@smuggle@macro\result
+ \egroup
+ \let\next\@gobble
+ \else
+ \@xp\ifx\space\testtok
+ \def\next{\@xp\futurelet\@xp\testtok\@xp\sseqnormalizemonomial@testend\romannumeral-`0}%
+ \else
+ \let\next\sseqnormalizemonomial@
+ \fi
+ \fi
+ \next
+}
+
+\def\sseqnormalizemonomial@varcs{sseqnormalizemonomial@var@\detokenize\@xp{\sseq@var}}
+
+\def\sseqnormalizemonomial@done{%
+ \def\result{}%
+ \def\\##1##2{%
+ \sseq@tempcount=\csname ##2\endcsname\relax
+ \ifnum\sseq@tempcount=\z@
+ \else
+ \ifnum\sseq@tempcount=\@ne
+ \sseq@d@addto@macro\result{##1}%
+ \else
+ \sseq@e@addto@macro\result{\unexpanded{##1}^{\the\sseq@tempcount}}%
+ \fi
+ \fi
+ }%
+ \sseq@support
+ \ifx\result\pgfutil@empty % use 1 for empty monomial
+ \def\result{1}%
+ \fi
+}
+
+% Restore catcode of underscore to letter
+\catcode`\_ = 11\relax
+
+
+\def\SseqAHSSNameHandler#1{
+ \pgfutil@in@[{#1}
+ \ifpgfutil@in@
+ \@xp\@firstofone
+ \else
+ \sseq@error{AHSSNameHandler-missing-cell}{#1}
+ \def\result{#1}
+ \@xp\@gobble
+ \fi{
+ \SseqAHSSNameHandler@#1\sseq@nil
+ }
+}
+
+
+\def\SseqAHSSNameHandler@#1[#2\sseq@nil{
+ \pgfutil@in@]{#2}
+ \ifpgfutil@in@
+ \@xp\@firstofone
+ \else
+ \sseq@error{AHSSNameHandler-missing-cell}{#1[#2}
+ \def\result{#1[#2}
+ \@xp\@gobble
+ \fi{
+ \SseqAHSSNameHandler@@{#1}#2\sseq@nil
+ }
+}
+
+\def\SseqAHSSNameHandler@@#1#2]#3\sseq@nil{
+ \SseqNormalizeMonomial{#1#3}
+ \sseq@ifintexpr{#2}{
+ \edef\result{\unexpanded\@xp{\result}[\the\numexpr#2+\sseq@y]}
+ }{
+ \sseq@error{AHSSNameHandler-invalid-integer}{#2}{#1[#2]#3}
+ \edef\result{\unexpanded\@xp{\result[#2]}}
+ }
+}
+
+
+
+
+\ExplSyntaxOff