__HEADER__(Grouping theorems for easier customization.) \ExplSyntaxOn \RequirePackage{amsthm} \RequirePackage{thmtools} \RequirePackage{mkessler-powerset} \hook_new:n { @@/prename } \hook_new:n { @@/postname } \hook_new:n { @@/mapname } \hook_new:n { @@/thmtools } \hook_new:n { @@/groupsort } %%TODO move this to fancythm \newcounter{indocument} \newcounter{insection}[section] \newcounter{insubsection}[subsection] \renewcommand\theinsection { \thesection.\arabic{insection} } \newcommand\theinsubection { \thessubection.\arabic{insubsection} } %%% LaTeX3 Wrappers around external commands \cs_new:Nn \@@_declare_theorem:nn { \declaretheorem [ #2 ] { #1 } } \cs_generate_variant:Nn \@@_declare_theorem:nn { n V } \cs_generate_variant:Nn \hook_gset_rule:nnnn { n n V n } %%% Messages \msg_new:nnn { groupthm } { unknown ~ key } { Unknown ~ key ~ '#1' ~ supplied ~ \msg_line_context: } \msg_new:nnn { groupthm } { unknown ~ group } { Unknown ~ group ~ '#1' ~ supplied ~ \msg_line_context: } %%% Variables \tl_new:N \l_@@_key_prename_tl \tl_new:N \l_@@_key_name_tl \tl_new:N \l_@@_key_postname_tl \clist_new:N \l_@@_key_group_clist \clist_new:N \l_@@_key_mapname_clist \clist_new:N \l_@@_key_thmtools_clist \tl_new:N \l_@@_prename_tl \tl_new:N \l_@@_name_tl \tl_new:N \l_@@_postname_tl \tl_new:N \l_@@_mapname_clist \tl_new:N \l_@@_thmtools_clist \tl_new:N \l_@@_relation_tl \clist_new:N \l_@@_group_clist \clist_new:N \g_groupthm_defined_theorem_groups_clist %%% Keys \keys_define:nn { groupthm } { prename .tl_set:N = \l_@@_key_prename_tl, prename .default:n = \c_empty_tl, prename .groups:n = { theoremgroup }, name .tl_set:N = \l_@@_key_name_tl, name .default:n = \c_novalue_tl, name .groups:n = { groupedtheorem, theoremvariants }, postname .tl_set:N = \l_@@_key_postname_tl, postname .default:n = \c_empty_tl, postname .groups:n = { theoremgroup }, group .clist_set:N = \l_@@_key_group_clist, group .default:n = {}, group .groups:n = { groupedtheorem, theoremvariants }, mapname .clist_set:N = \l_@@_mapname_clist, mapname .default:n = {}, mapname .groups:n = { theoremgroup }, thmtools .clist_set:N = \l_@@_key_thmtools_clist, thmtools .default:n = {}, thmtools .groups:n = { theoremgroup, groupedtheorem, theoremvariants }, unknown .code:n = \msg_error:nnn { groupthm } { unknown ~ group } { \l_keys_key_str } } % groupname, prename, postname, mapname, thmtools \cs_new:Npn \new_theorem_group:nnnnn #1#2#3#4#5 { \cs_new:cpn { group_use_#1: } { \hook_gput_code:nnn { @@/prename } { #1 } { \tl_put_left:Nx \l_@@_prename_tl { #2 } } \hook_gput_code:nnn { @@/postname } { #1 } { \tl_put_right:Nx \l_@@_postname_tl { #3 } } \hook_gput_code:nnn { @@/mapname } { #1 } { \clist_put_right:Nn \l_@@_mapname_clist { #4 } } \hook_gput_code:nnn { @@/thmtools } { #1 } { \clist_put_right:Nn \l_@@_thmtools_clist { #5 } } } \clist_gput_left:Nn \g_groupthm_defined_theorem_groups_clist { #1 } } \cs_generate_variant:Nn \new_theorem_group:nnnnn { n V V V V } \cs_new:Npn \@@_undeclare_theorem_group_aux:n #1 { \cs_undefine:c { @@_use_group_#1: } \hook_gremove_code:nn { @@/prename } \hook_gremove_code:nn { @@/postname } \hook_gremove_code:nn { @@/mapname } \hook_gremove_code:nn { @@/thmtools } \clist_remove_all:Nn \g_@@_defined_theorem_groups_clist { #1 } \@@_hook_gset_rule_foreach:nNnn { ?? } \g_@@_defined_theorem_groups_clist { #1 } { unrelated } { #1 } \@@_hook_gset_rule_foreach:nNnn { @@/prename } \g_@@_defined_theorem_groups_clist { #1 } { unrelated } { #1 } \@@_hook_gset_rule_foreach:nNnn { @@/postname } \g_@@_defined_theorem_groups_clist { #1 } { unrelated } { #1 } \@@_hook_gset_rule_foreach:nNnn { @@/mapname } \g_@@_defined_theorem_groups_clist { #1 } { unrelated } { #1 } \@@_hook_gset_rule_foreach:nNnn { @@/thmtools } \g_@@_defined_theorem_groups_clist { #1 } { unrelated } { #1 } } % hook, list of labels, relation, label \cs_new:Npn \@@_hook_gset_rule_foreach:nNnn #1 #2 #3 #4 { \cs_set:Npn \@@_map_aux:n { \hooks_gset_rule:nnnn { #1 } { ##1 } { #3 } { #4 } } \clist_map_function:NN #2 \@@_map_aux:n } \cs_new:Npn \@@_update_ordering:n #1 { \@@_hook_gset_rule_foreach:nNnn { @@/groupsort } \g_@@_defined_theorem_groups_clist { before } { #1 } } \cs_new:Npn \new_theorem_group_by_keys:nn #1#2 { \@@_set_normalized_keys:nnn { #1 } { theoremgroup } { #2 } \new_theorem_group:nVVVV { #1 } \l_@@_prename_tl \l_@@_postname_tl \l_@@_mapname_clist \l_@@_thmtools_clist } \NewDocumentCommand{\NewTheoremGroup}{ O{} m } { \new_theorem_group_by_keys:nn { #2 } { #1 } } \cs_new:Npn \groupthm_new_theorem_group:nnnnn #1 #2 #3 #3 #5 { \cs_if_exist:cTF { @@_use_group_#1 } { \msg_error:nnnn { groupthm } { wrong ~ definition } { group } { #1 } { already } } { \@@_new_theorem_aux:nnnnn { #1 } { #2 } { #3 } { #4 } { #5 } } } \cs_new:Npn \groupthm_renew_theorem_group:nnnnn #1 #2 #3 #3 #5 { \cs_if_exist:cTF { @@_use_group_#1 } { \@@_undeclare_theorem_aux:nnnnn { #1 } \@@_declare_theorem_aux:nnnnn { #1 } { #2 } { #3 } { #4 } { #5 } } { \msg_error:nnnn { groupthm } { wrong ~ definition } { group } { #1 } { not } } } \cs_new:Npn \groupthm_provide_theorem_group:nnnnn #1 #2 #3 #3 #5 { \cs_if_exist:cF { @@_use_group_#1 } { \@@_new_theorem_aux:nnnnn { #1 } { #2 } { #3 } { #4 } { #5 } } } \cs_new:Npn \groupthm_declare_theorem_group:nnnnn #1 #2 #3 #3 #5 { \cs_if_exist:cT { @@_use_group_#1 } { \@@_undeclare_theorem_aux:nnnnn { #1 } } \@@_declare_theorem_aux:nnnnn { #1 } { #2 } { #3 } { #4 } { #5 } } %%% Documented until here %%% Grouped Theorems \cs_new:Npn \group_use:n #1 { \cs_if_exist_use:cF { group_use_#1: } { \msg_error:nnn { groupthm } { unknown ~ group } { #1 } } } \cs_new:Npn \map_use_on_name:n #1 { \tl_set:Nx \l_@@_name_tl { #1 { \tl_use:N \l_@@_name_tl } } } % envname, groups, name, thmtools \cs_new:Npn \new_grouped_theorem:nnnn #1 #2 #3 #4 { \tl_clear:N \l_@@_prename_tl \tl_set:Nn \l_@@_name_tl { #3 } \tl_clear:N \l_@@_postname_tl \clist_clear:N \l_@@_mapname_clist \clist_set:Nn \l_@@_thmtools_clist { #4 } \hook_gremove_code:nn { @@/prename }{*} \hook_gremove_code:nn { @@/postname }{*} \hook_gremove_code:nn { @@/mapname }{*} \hook_gremove_code:nn { @@/thmtools }{*} \clist_map_function:nN { #2 } \group_use:n % \ShowHook{ @@/postname } \hook_use:n { @@/prename } \hook_use:n { @@/postname } \hook_use:n { @@/mapname } \hook_use:n { @@/thmtools } \clist_map_function:NN \l_@@_mapname_clist \map_use_on_name:n \clist_put_right:Nx \l_@@_thmtools_clist { name = \tl_use:N \l_@@_prename_tl \tl_use:N \l_@@_name_tl \tl_use:N \l_@@_postname_tl } \@@_declare_theorem:nV { #1 } \l_@@_thmtools_clist } \cs_generate_variant:Nn \new_grouped_theorem:nnnn { n V V V } \cs_generate_variant:Nn \new_grouped_theorem:nnnn { x V n n } \cs_new:Npn \groupthm_provide_grouped_theorem:nnnn { \cs_if_exist:cF { #1 } { \@@_groupthm_declare_grouped_theorem_aux:nnnn { #1 } { #2 } { #3 } { #4 } } } \cs_new:Npn \groupthm_new_theorem:nnnn #1 #2 #3 #4 { \groupthm_new_grouped_theorem:nnnn { #1 } { #2 } { #3 } { #4 } \groupthm_new_grouped_theorem:nnnn { #1* } { #2, starred } { #3 } { #4 } } % keys, subgroup \cs_new:Npn \@@_set_normalized_keys:nnn #1 #2 #3 { \keys_set:nn { @@ } { prename, name, postname, group, mapname, thmtools } \keys_set_groups:nnn { @@ } { #2 } { #1 } \tl_if_eq:NnTF \l_@@_key_name_tl { \c_novalue_tl } { \tl_set:Nx \l_@@_name_tl { \text_titlecase_first:n {#3} } } { \tl_set_eq:NN \l_@@_name_tl \l_@@_key_name_tl } \tl_set_eq:NN \l_@@_prename_tl \l_@@_key_prename_tl \tl_set_eq:NN \l_@@_postname_tl \l_@@_key_postname_tl \clist_set_eq:NN \l_@@_group_clist \l_@@_key_group_clist \clist_set_eq:NN \l_@@_mapname_clist \l_@@_key_mapname_clist \clist_set_eq:NN \l_@@_thmtools_clist \l_@@_key_group_clist } \cs_new:Npn \groupthm_provide_theorem_star:nnnn #1 #2 #3 #4 { \groupthm_provide_theorem:nnnn { #1 } { #2, unnumbered } { #3 } { #4 } } % envname, keys \cs_new:Npn \new_grouped_theorem_from_keys:nn #1 #2 { \@@_set_normalized_keys { #2 } { groupedtheorem } { #1 } \new_grouped_theorem:nVVV { #1 } \l_@@_key_group_clist \l_@@_name_tl \l_@@_key_thmtools_clist } \NewDocumentCommand{\NewGroupedTheorem}{O{} m} { \new_grouped_theorem_from_keys:nn { #2 } { #1 } } \groupthm_new_grouped_theorem_from_keys:nn { } %% Rules for different theorem groups \cs_generate_variant:Nn \hook_gset_rule:nnnn { n n V n } % hook group1 relation group2 \cs_new:Npn \declare_theorem_group_rule:nnnn #1 #2 #3 #4 { \str_set:Nx \l_tmpa_str { \tl_trim_spaces:n { #3 } } \str_if_eq:VnT \l_tmpa_str { higher } { \str_set:Nn \l_tmpa_tl { after } } \str_if_eq:VnT \l_tmpa_str{ lower } { \str_set:Nn \l_tmpa_tl { before } } \str_if_eq:nnTF { #1 } { ?? } { \hook_gset_rule:nnVn {??} {#2} \l_tmpa_tl {#4} } { \hook_gset_rule:nnVn { @@ / #1 } {#2} \l_tmpa_tl {#4} } } % hook, group1, relation, group2 \NewDocumentCommand { \DeclareTheoremGroupRule } { O{??} m m m } { \declare_theorem_group_rule:nnnn {#1} {#2} {#3} {#4} } %%% Hacks for sorting groupnames \cs_new:Npn \__add_to_sort_hook:n #1 { \hook_gput_code:nnn { @@/groupsort } { #1 } { \clist_put_left:Nn \l_@@_group_clist { #1 } } } \cs_new:Npn \__sort_group_names: { \hook_gremove_code:nn { @@/groupsort }{*} \clist_map_function:NN \l_@@_group_clist \__add_to_sort_hook:n \clist_clear:N \l_@@_group_clist \hook_use:n { @@/groupsort } } %%% Theorem variants generation \ExplSyntaxOn % envname, groups, name, thmtools list, extra groups (not in powerset) \cs_new:Npn \@@_new_grouped_theorem_family_aux:nnnnnn { \clist_set:Nn \l_tmpa_clist { #2 } \powerset_clist_foreach:Nn \l_tmpa_clist { \clist_set_eq:NN \l_@@_group_clist \l_tmpa_clist \clist_put_right:Nn \l_@@_group_clist { #5 } \__sort_group_names: \use:c{groupthm_#6_grouped_theorem:xVnn} {__#1__groups_\clist_use:Nn \l_@@_group_clist {_}} \l_@@_group_clist { #3 } { #4 } } } % envname, name, thmtools, list of groups \cs_new:Npn \generate_theorem_variants:nnnn #1 #2 #3 #4 { \clist_set:Nn \l_@@_group_clist { #4 } \powerset_clist_foreach:Nn \l_@@_group_clist { \__sort_group_names: \new_grouped_theorem:xVnn {__#1__groups_\clist_use:Nn \l_@@_group_clist {_}} \l_@@_group_clist { #2 } { #3 } } } \cs_generate_variant:Nn \generate_theorem_variants:nnnn { n V V V } \cs_new:Npn \groupthm_new_grouped_theorem_family_from_keys:nn #1 #2 { \@@_set_normalized_keys:nn { #1 } { #2 } \groupthm_new_grouped_theorem_family:nVVV { #2 } \l_@@_groups_clist \l_@@_name_tl \l_@@_thmtools_clist } % envname, keys \cs_new:Npn \generate_theorem_variants_from_keys:nn #1 #2 { \keys_set_groups:nnn { groupthm } { theoremvariants } { name, thmtools, group } \keys_set_groups:nnn { groupthm } { theoremvariants } { #2 } \tl_if_eq:NnTF \l_@@_key_name_tl { \c_novalue_tl } { \tl_set:Nx \l_@@_name_tl { \text_titlecase_first:n {#1} } } { \tl_set_eq:NN \l_@@_name_tl \l_@@_key_name_tl } \clist_set_eq:NN \l_@@_group_clist \l_@@_key_group_clist \clist_put_left:Nn \l_@@_group_clist { starred } \generate_theorem_variants:nVVV { #1 } \l_@@_name_tl \l_@@_key_thmtools_clist \l_@@_group_clist } \NewDocumentCommand { \GenerateTheoremVariants } { O{} m } { \generate_theorem_variants_from_keys:nn { #2 } { #1 } } %%% Theorem variants declaration / parsing \cs_new:Npn \add_theorem_to_group:n #1 { \bool_if:NTF \l_@@_in_family_options_environment_bool { \clist_put_left:Nn \l_@@_group_clist { #1 } } { \msg_error:nn { groupthm } { misuse ~ add ~ theorem ~ to ~ group } } } % envname, signature, definition, always groups, backend \cs_new:Npn \__new_theorem_variant_parser_aux:nnnn #1 #2 #3 #4 #5 { \use:c{ #5 DocumentEnvironment } { #1 } { #2 } { \clist_clear:N \l_@@_group_clist #3 \clist_put_right:NV \l_@@_group_clist { #4 } \__sort_group_names: \begin { __#1__groups_ \clist_use:Nn \l_@@_group_clist { _ } } } { \clist_clear:N \l_@@_group_clist #3 \clist_put_right:NV \l_@@_group_clist { #4 } \__sort_group_names: \end { __#1__groups_ \clist_use:Nn \l_@@_group_clist { _ } } } } % envname, signature, definition \cs_new:Npn \new_theorem_variant_parser:nnn #1 #2 #3 { \__new_theorem_variant_parser_aux:nnnn { #1 } { #2 } { #3 } { \BooleanTrue } \__new_theorem_variant_parser_aux:nnnn { #1 } { #2 } { #3 } { \BooleanFalse } } %% Exposing clean interface for parsing theorem variants \NewDocumentCommand { \AddTheoremToGroup } { m } { \add_theorem_to_group:n { #1 } } \NewDocumentCommand{ \DeclareTheoremVariants }{ m m m } { \new_theorem_variant_parser:nnn { #1 } { #2 } { #3 } } %%%%% Convenience macros for usage of this package % evname, star? \cs_new:Npn \declare_standard_theorem_variants:n #1 { \DeclareTheoremVariants { #1 } { !s !t+ } { \IfBooleanT{##1} { \AddTheoremToGroup { star } } \IfBooleanT{##2} { \AddTheoremToGroup { dagger } } } } % envname, name, thmtools \cs_new:Npn \generate_standard_theorem_variants:nnn #1 #2 #3 { \generate_theorem_variants:nnnn { #1 } { #2 } { #3 } { star, dagger, starred } } % envname, keys \cs_new:Npn \generate_standard_theorem_variants_from_keys:nn { % TODO } \NewDocumentCommand{\GenerateDefaultTheoremVariants}{ O{} m } { \generate_standard_theorem_variants_from_keys:nn { #2 } { #1 } } %%% Default groups available \NewTheoremGroup [ ]{ all } \NewTheoremGroup [ thmtools = { numbered = no } ] { starred } \NewTheoremGroup [ thmtools = { numbered = no } ] { unnumbered } %% \NewTheoremGroup [ thmtools = { sibling = insection } ] { big } \NewTheoremGroup [ postname = { $^{\dagger}$ }, thmtools = { sibling = insubsection } ] { dagger } \NewTheoremGroup [ thmtools = { sibling = insubsection } ] { small } \NewTheoremGroup [ thmtools = { sibling = insubsection }, postname = { * }, ] { star } \DeclareTheoremGroupRule { big } { incompatible-error } { small } \DeclareTheoremGroupRule { all } { lower } { big } \DeclareTheoremGroupRule { all } { lower } { small } \DeclareTheoremGroupRule { all } { lower } { dagger } \DeclareTheoremGroupRule { all } { lower } { star } \DeclareTheoremGroupRule { starred } { higher } { big } \DeclareTheoremGroupRule { starred } { higher } { small } \DeclareTheoremGroupRule { starred } { higher } { dagger } \DeclareTheoremGroupRule { starred } { higher } { star }