summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/xsim/code
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/latex/contrib/xsim/code
Initial commit
Diffstat (limited to 'macros/latex/contrib/xsim/code')
-rw-r--r--macros/latex/contrib/xsim/code/xsim.auxfile.code.tex186
-rw-r--r--macros/latex/contrib/xsim/code/xsim.base.code.tex174
-rw-r--r--macros/latex/contrib/xsim/code/xsim.blanks.code.tex161
-rw-r--r--macros/latex/contrib/xsim/code/xsim.collections.code.tex396
-rw-r--r--macros/latex/contrib/xsim/code/xsim.definitions.code.tex431
-rw-r--r--macros/latex/contrib/xsim/code/xsim.environments.code.tex380
-rw-r--r--macros/latex/contrib/xsim/code/xsim.exercises.code.tex556
-rw-r--r--macros/latex/contrib/xsim/code/xsim.goals.code.tex307
-rw-r--r--macros/latex/contrib/xsim/code/xsim.grades.code.tex153
-rw-r--r--macros/latex/contrib/xsim/code/xsim.interface.code.tex649
-rw-r--r--macros/latex/contrib/xsim/code/xsim.modules.code.tex212
-rw-r--r--macros/latex/contrib/xsim/code/xsim.properties.code.tex496
-rw-r--r--macros/latex/contrib/xsim/code/xsim.random.code.tex317
-rw-r--r--macros/latex/contrib/xsim/code/xsim.solutions.code.tex407
-rw-r--r--macros/latex/contrib/xsim/code/xsim.sty109
-rw-r--r--macros/latex/contrib/xsim/code/xsim.style.layouts.code.tex77
-rw-r--r--macros/latex/contrib/xsim/code/xsim.tags.code.tex211
-rw-r--r--macros/latex/contrib/xsim/code/xsim.templates.code.tex172
-rw-r--r--macros/latex/contrib/xsim/code/xsim.translations.code.tex146
-rw-r--r--macros/latex/contrib/xsim/code/xsim.verbwrite.code.tex209
-rw-r--r--macros/latex/contrib/xsim/code/xsimverb.sty93
21 files changed, 5842 insertions, 0 deletions
diff --git a/macros/latex/contrib/xsim/code/xsim.auxfile.code.tex b/macros/latex/contrib/xsim/code/xsim.auxfile.code.tex
new file mode 100644
index 0000000000..e5eff8b728
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsim.auxfile.code.tex
@@ -0,0 +1,186 @@
+% ----------------------------------------------------------------------------
+% the XSIM package - auxfile module
+%
+% eXercise Sheets IMproved
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+\XSIMmodule{auxfile}{writing stuff to the aux file}
+
+\cs_new_protected:Npn \xsim_write_to_aux:n #1
+ { \iow_now:Nn \@auxout {#1} }
+\cs_generate_variant:Nn \xsim_write_to_aux:n {x}
+
+% --------------------------------------------------------------------------
+
+\cs_new_protected:Npn \XSIM #1 { \use:c {XSIMaux#1} }
+
+\AtBeginDocument
+ {
+ \xsim_write_to_aux:x
+ { \token_to_str:N \providecommand \token_to_str:N \XSIM [1] {} }
+ }
+
+\cs_new_protected:Npn \xsim_new_aux_cs:cpn #1
+ { \cs_new_protected:cpn {XSIMaux#1} }
+
+\cs_new_protected:Npn \xsim_add_cs_to_aux:nn #1#2
+ {
+ \xsim_verbose:n { Writing~ command~ \XSIM {#1}~ to~ aux~ file }
+ \xsim_write_to_aux:x { \XSIM {#1} #2 }
+ }
+
+% ----------------------------------------------------------------------------
+% list for recording values that need to be written to the aux file and
+% updated at begin document
+
+\str_new:N \l__xsim_split_aux_lists_str
+\keys_define:nn {xsim}
+ {
+ split-aux-lists .code:n = \str_set:Nn \l__xsim_split_aux_lists_str {#1} ,
+ split-aux-lists .initial:n = ||
+ }
+
+% define list:
+% #1: name
+\cs_new_protected:Npn \xsim_new_list:n #1
+ {
+ \xsim_verbose:n { Defining~ new~ auxfile~ list~ `#1'. }
+ \prop_new:c {g__xsim_list_#1_prop}
+ \seq_new:c {g__xsim_list_#1_seq}
+ \seq_new:c {g__xsim_list_recorded_#1_seq}
+ \xsim_new_aux_cs:cpn {#1} ##1
+ {
+ \prop_gclear:c {g__xsim_list_#1_prop}
+ \tl_if_blank:nF {##1}
+ {
+ \seq_gset_split:cVx {g__xsim_list_#1_seq}
+ \l__xsim_split_aux_lists_str
+ { \tl_to_str:n {##1} }
+ \seq_map_inline:cn {g__xsim_list_#1_seq}
+ { \prop_gput:cnn {g__xsim_list_#1_prop} {####1} {} }
+ }
+ }
+ \AtBeginDocument
+ {
+ \seq_gclear:c {g__xsim_list_recorded_#1_seq}
+ \seq_set_eq:Nc \l__xsim_tmpa_seq {g__xsim_list_#1_seq}
+ \seq_map_inline:Nn \l__xsim_tmpa_seq
+ {
+ \str_set:Nn \l__xsim_tmpa_str {##1}
+ \seq_gput_right:cV {g__xsim_list_recorded_#1_seq} \l__xsim_tmpa_str
+ }
+ \seq_gremove_duplicates:c {g__xsim_list_recorded_#1_seq}
+ }
+ }
+
+% add to or remove from list:
+% #1: name
+% #2: entry
+\cs_new_protected:Npn \xsim_add_to_list:nn #1#2
+ {
+ \xsim_verbose:n { Adding~ `#2'~ to~ list~ `#1'. }
+ \str_set:Nn \l__xsim_tmpa_str {#2}
+ \prop_gput:cnn {g__xsim_list_#1_prop} {#2} {}
+ \seq_if_in:cVT {g__xsim_list_#1_seq} \l__xsim_tmpa_str
+ { \seq_gremove_all:cV {g__xsim_list_#1_seq} \l__xsim_tmpa_str }
+ \seq_gput_right:cV {g__xsim_list_#1_seq} \l__xsim_tmpa_str
+ }
+\cs_generate_variant:Nn \xsim_add_to_list:nn {nx}
+
+% #1: name
+% #2: entry
+\cs_new_protected:Npn \xsim_remove_from_list:nn #1#2
+ {
+ \xsim_verbose:n { Removing~ `#2'~ to~ list~ `#1'. }
+ \str_set:Nn \l__xsim_tmpa_str {#2}
+ \prop_if_in:cnT {g__xsim_list_#1_prop} {#2}
+ { \prop_gremove:cn {g__xsim_list_#1_prop} {#2} }
+ \seq_if_in:cVT {g__xsim_list_#1_seq} \l__xsim_tmpa_str
+ { \seq_gremove_all:cV {g__xsim_list_#1_seq} \l__xsim_tmpa_str }
+ \seq_if_in:cVT {g__xsim_list_recorded#1_seq} \l__xsim_tmpa_str
+ { \seq_gremove_all:cV {g__xsim_list_recorded_#1_seq} \l__xsim_tmpa_str }
+ }
+\cs_generate_variant:Nn \xsim_remove_from_list:nn {nx}
+
+% check if in list:
+% #1: name
+% #2: entry
+\prg_new_conditional:Npnn \xsim_if_in_list:nn #1#2 {p,T,F,TF}
+ {
+ \prop_if_in:cnTF {g__xsim_list_#1_prop} {#2}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\cs_generate_variant:Nn \xsim_if_in_list:nnTF {nx}
+\cs_generate_variant:Nn \xsim_if_in_list:nnT {nx}
+
+\cs_new:Npn \xsim_list_show:n #1
+ { \prop_show:c {g__xsim_list_#1_prop} }
+
+\bool_new:N \l__xsim_empty_lists_bool
+\cs_new_protected:Npn \xsim_empty_lists:
+ { \bool_set_true:N \l__xsim_empty_lists_bool }
+
+% update list -- should be used inside \AtEndDocument:
+% #1: name
+\cs_new_protected:Npn \xsim_update_list:n #1
+ {
+ \xsim_verbose:n { Updating~ list~ `#1'. }
+ \seq_remove_duplicates:c {g__xsim_list_#1_seq}
+ \seq_remove_duplicates:c {g__xsim_list_recorded_#1_seq}
+ \str_set:Nx \l__xsim_tmpa_str
+ { \seq_use:cn {g__xsim_list_#1_seq} {} }
+ \str_set:Nx \l__xsim_tmpb_str
+ { \seq_use:cn {g__xsim_list_recorded_#1_seq} {} }
+ \str_if_eq:NNF \l__xsim_tmpa_str \l__xsim_tmpb_str
+ { \xsim_rerun: }
+ \bool_if:NF \l__xsim_empty_lists_bool
+ {
+ \xsim_add_cs_to_aux:nn {#1}
+ { { \seq_use:cV {g__xsim_list_#1_seq} \l__xsim_split_aux_lists_str } }
+ }
+ }
+
+% loop over list:
+% #1: name
+% #2: code
+\cs_new_protected:Npn \xsim_foreach_list_entry:nn #1#2
+ { \seq_map_inline:cn {g__xsim_list_recorded_#1_seq} {#2} }
+
+\cs_new_protected:Npn \xsim_foreach_new_list_entry:nn #1#2
+ { \seq_map_inline:cn {g__xsim_list_#1_seq} {#2} }
+
+% length of a list:
+% #1: name
+\cs_new:Npn \xsim_list_count:n #1
+ { \seq_count:c {g__xsim_list_recorded_#1_seq} }
+
+% item of a list:
+% #1: name
+% #2: integer
+\cs_new:Npn \xsim_list_item:nn #1#2
+ { \seq_item:cn {g__xsim_list_recorded_#1_seq} {#2} }
+
+% ----------------------------------------------------------------------------
+\file_input_stop:
diff --git a/macros/latex/contrib/xsim/code/xsim.base.code.tex b/macros/latex/contrib/xsim/code/xsim.base.code.tex
new file mode 100644
index 0000000000..32fee6e1bd
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsim.base.code.tex
@@ -0,0 +1,174 @@
+% ----------------------------------------------------------------------------
+% the XSIM package - base module
+%
+% eXercise Sheets IMproved
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+\XSIMmodule{base}{basic functionality of the package}
+
+\xsim_load_modules:n {auxfile}
+
+% ----------------------------------------------------------------------------
+% variants of kernel functions:
+\cs_generate_variant:Nn \seq_set_split:Nnn {Nnx,NV}
+\cs_generate_variant:Nn \seq_gset_split:Nnn {c,cVx,cnx}
+\cs_generate_variant:Nn \seq_use:Nn {cV}
+\cs_generate_variant:Nn \seq_count:N {c}
+\cs_generate_variant:Nn \seq_if_empty:NT {c}
+\cs_generate_variant:Nn \seq_if_in:NnT {cV}
+\cs_generate_variant:Nn \seq_gremove_all:Nn {cV}
+\cs_generate_variant:Nn \seq_gput_right:Nn {cV}
+\cs_generate_variant:Nn \prop_put:Nnn {cxx}
+\cs_generate_variant:Nn \prop_gput:Nnn {Nx,Nnx,cxx}
+\cs_generate_variant:Nn \prop_item:Nn {c}
+\cs_generate_variant:Nn \prop_if_in:NnTF {Nx}
+\cs_generate_variant:Nn \use:nn {nx}
+\cs_generate_variant:Nn \file_input:n {V,x}
+\cs_generate_variant:Nn \file_if_exist:nT {V,x}
+\cs_generate_variant:Nn \file_if_exist:nTF {V}
+\cs_generate_variant:Nn \msg_error:nnnnn {nnnxx}
+\cs_generate_variant:Nn \keys_define:nn {nx}
+\cs_generate_variant:Nn \keys_set:nn {xn}
+\cs_generate_variant:Nn \tl_mixed_case:n {x,f}
+\cs_generate_variant:Nn \tl_if_eq:nnTF {ff,V}
+\cs_generate_variant:Nn \tl_if_eq:nnT {x}
+\cs_generate_variant:Nn \tl_if_blank:nTF {f}
+\cs_generate_variant:Nn \tl_set_rescan:Nnn {Nnn}
+\cs_generate_variant:Nn \iow_now:Nn {NV}
+\cs_generate_variant:Nn \str_if_eq:nnT {x}
+\cs_generate_variant:Nn \iow_now:Nn {NV}
+\cs_generate_variant:Nn \prg_new_protected_conditional:Npnn {c}
+
+% ----------------------------------------------------------------------------
+% temporary variables:
+\tl_new:N \l__xsim_tmpa_tl
+\tl_new:N \l__xsim_tmpb_tl
+\tl_new:N \l__xsim_tmpc_tl
+\tl_new:N \l__xsim_tmpd_tl
+
+\str_new:N \l__xsim_tmpa_str
+\str_new:N \l__xsim_tmpb_str
+\str_new:N \l__xsim_tmpc_str
+
+\bool_new:N \l__xsim_tmpa_bool
+\bool_new:N \l__xsim_tmpb_bool
+\bool_new:N \l__xsim_tmpc_bool
+
+\dim_new:N \l__xsim_tmpa_dim
+\dim_new:N \l__xsim_tmpb_dim
+\dim_new:N \l__xsim_tmpc_dim
+
+\seq_new:N \l__xsim_tmpa_seq
+\seq_new:N \l__xsim_tmpb_seq
+\seq_new:N \l__xsim_tmpc_seq
+
+\int_new:N \l__xsim_tmpa_int
+\int_new:N \l__xsim_tmpb_int
+\int_new:N \l__xsim_tmpc_int
+
+\box_new:N \l__xsim_tmpa_box
+\box_new:N \l__xsim_tmpb_box
+\box_new:N \l__xsim_tmpc_box
+
+\clist_new:N \l__xsim_tmpa_clist
+\clist_new:N \l__xsim_tmpb_clist
+\clist_new:N \l__xsim_tmpc_clist
+
+% ----------------------------------------------------------------------------
+
+\prg_new_conditional:Npnn \xsim_if_komascript: {T,F,TF}
+ {
+ \cs_if_exist:cTF {KOMAClassName}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+% ----------------------------------------------------------------------------
+
+\prg_new_conditional:Npnn \xsim_if_final: {p,T,F,TF}
+ {
+ \bool_if:NTF \g_xsim_final_bool
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+\prg_new_conditional:Npnn \xsim_if_verbose: {p,T,F,TF}
+ {
+ \bool_if:NTF \g_xsim_verbose_bool
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+\prg_new_conditional:Npnn \xsim_if_rerun: {p,T,F,TF}
+ {
+ \bool_if:NTF \g__xsim_rerun_bool
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+\prg_new_conditional:Npnn \xsim_if_clear_aux: {p,T,F,TF}
+ {
+ \xsim_if_final:TF
+ { \prg_return_false: }
+ {
+ \bool_if:NTF \g_xsim_clear_aux_bool
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+ }
+
+\cs_new_protected:Npn \xsim_verbose:n #1
+ { \xsim_if_verbose:T { \msg_info:nnn {xsim} {verbose} {#1} } }
+\cs_generate_variant:Nn \xsim_verbose:n {x}
+
+\cs_new_protected:Npn \xsim_rerun:
+ { \bool_gset_true:N \g__xsim_rerun_bool }
+
+\prg_new_conditional:Npnn \xsim_if_chapter: {p,T,F,TF}
+ {
+ \bool_if:nTF
+ { \cs_if_exist_p:N \chapter && \cs_if_exist_p:N \c@chapter }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+% ----------------------------------------------------------------------------
+
+% #1: macro name
+% #2: 0 to 9 times `n' (= number of arguments of internal item)
+% #3: code; may contain a suiting \__xsim_loop_item:<nnn>
+\cs_new_protected:Npn \xsim_define_loop_macro:Nnn #1#2#3
+ {
+ \xsim_verbose:n { Defining~ loop~ macro~ #1 }
+ \cs_new_protected:Npn #1 ##1##2
+ { \cs_set:cn {__xsim_loop_item:#2} {##2} #3 }
+ }
+
+% ----------------------------------------------------------------------------
+
+\cs_new_protected:Npn \xsim_setup:n #1
+ { \keys_set:nn {xsim} {#1} }
+
+% ----------------------------------------------------------------------------
+\file_input_stop:
diff --git a/macros/latex/contrib/xsim/code/xsim.blanks.code.tex b/macros/latex/contrib/xsim/code/xsim.blanks.code.tex
new file mode 100644
index 0000000000..97a37e2f71
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsim.blanks.code.tex
@@ -0,0 +1,161 @@
+% ----------------------------------------------------------------------------
+% the XSIM package - blanks module
+%
+% eXercise Sheets IMproved
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+\XSIMmodule{blanks}{add blanks, cloze}
+
+\bool_new:N \l__xsim_blank_width_bool
+\bool_new:N \l__xsim_blank_linespread_bool
+\bool_new:N \l__xsim_fill_blank_bool
+\tl_new:N \l__xsim_blank_linespread_tl
+\tl_new:N \l__xsim_blank_scale_tl
+\dim_new:N \l__xsim_blank_dim
+\dim_new:N \l__xsim_blank_line_increment_dim
+\dim_new:N \l__xsim_blank_line_minimum_length_dim
+\box_new:N \l__xsim_blank_box
+
+\cs_new_protected:Npn \xsim_write_cloze_blank:n #1 {#1}
+\cs_new_protected:Npn \xsim_write_cloze_filled:n #1 {#1}
+
+\keys_define:nn {xsim/blank}
+ {
+ blank-style .code:n =
+ \cs_set_protected:Npn \xsim_write_cloze_blank:n ##1 {#1} ,
+ blank-style .initial:n = \underline {#1} ,
+ filled-style .code:n =
+ \cs_set_protected:Npn \xsim_write_cloze_filled:n ##1 {#1} ,
+ filled-style .initial:n = \underline {#1} ,
+ style .meta:n =
+ {
+ blank-style = #1 ,
+ filled-style = #1
+ } ,
+ fill .bool_set:N = \l__xsim_fill_blank_bool ,
+ fill .initial:n = false ,
+ scale .tl_set:N = \l__xsim_blank_scale_tl ,
+ scale .initial:n = 1 ,
+ width .code:n =
+ {
+ \bool_set_true:N \l__xsim_blank_width_bool
+ \dim_set:Nn \l__xsim_blank_dim {#1}
+ } ,
+ linespread .code:n =
+ \bool_set_true:N \l__xsim_blank_linespread_bool
+ \tl_set:Nn \l__xsim_blank_linespread_tl {#1} ,
+ linespread .initial:n = 1 ,
+ line-increment .dim_set:N = \l__xsim_blank_line_increment_dim ,
+ line-increment .initial:n = 1pt ,
+ line-minimum-length .dim_set:N = \l__xsim_blank_line_minimum_length_dim ,
+ line-minimum-length .initial:n = 2em
+ }
+
+\cs_new_protected:Npn \xsim_blank:n #1
+ {
+ \box_clear:N \l__xsim_blank_box
+ \mode_if_math:TF
+ { \hbox_set:Nn \l__xsim_blank_box { $ \m@th \mathpalette{}{#1} $ } }
+ { \hbox_set:Nn \l__xsim_blank_box {#1} }
+ \bool_if:nTF
+ { \xsim_if_inside_solution_p: || \l__xsim_fill_blank_bool }
+ { \xsim_write_cloze_filled:n {#1} }
+ {
+ \bool_if:NTF \l__xsim_blank_width_bool
+ { \__xsim_blank_skip:V \l__xsim_blank_dim }
+ { \__xsim_blank_skip:n { \box_wd:N \l__xsim_blank_box } }
+ }
+ }
+
+\cs_new_protected:Npn \__xsim_blank_skip:n #1
+ {
+ \bool_if:NTF \l__xsim_blank_width_bool
+ { \dim_set:Nn \l__xsim_tmpa_dim {#1} }
+ {
+ \fp_set:Nn \l__xsim_tmpa_fp
+ { \dim_to_fp:n {#1} * \l__xsim_blank_scale_tl }
+ \dim_set:Nn \l__xsim_tmpa_dim { \fp_to_dim:N \l__xsim_tmpa_fp }
+ }
+ \dim_compare:nTF
+ { \l__xsim_tmpa_dim > \l__xsim_blank_line_minimum_length_dim }
+ {
+ \mode_if_math:TF
+ { \xsim_write_cloze_blank:n { \skip_horizontal:N \l__xsim_tmpa_dim } }
+ {
+ \dim_do_while:nn { \l__xsim_tmpa_dim > \c_zero_dim }
+ {
+ % I wonder what the correct l3 way would be -- if there is
+ % one, yet:
+ % \tex_hfil:D
+ \tex_penalty:D \hyphenpenalty
+ % \tex_hfilneg:D
+ \dim_compare:nTF
+ { \l__xsim_tmpa_dim < \l__xsim_blank_line_increment_dim }
+ { \xsim_write_cloze_blank:n { \skip_horizontal:N \l__xsim_tmpa_dim } }
+ {
+ \xsim_write_cloze_blank:n
+ { \skip_horizontal:N \l__xsim_blank_line_increment_dim }
+ }
+ \dim_sub:Nn \l__xsim_tmpa_dim { \l__xsim_blank_line_increment_dim }
+ }
+ }
+ }
+ {
+ \xsim_write_cloze_blank:n
+ { \skip_horizontal:N \l__xsim_blank_line_minimum_length_dim }
+ }
+ }
+\cs_generate_variant:Nn \__xsim_blank_skip:n { V }
+
+% ----------------------------------------------------------------------------
+\file_input_stop:
+
+the following code from Heiko Oberdieck in d.c.t.t served as inspiration
+and basis for the \blank command:
+https://groups.google.com/d/msg/de.comp.text.tex/fZLwraH04jE/o1RSdFXjGuIJ
+
+\makeatletter
+\newcommand*{\luecke}{%
+ \begingroup
+ \setlength{\dimen@}{6cm}%
+ \ifdim\dimen@>2em %
+ \underline{\hspace{1em}}%
+ \advance\dimen@ by -2em\relax
+ \@whiledim\dimen@>0pt\do{%
+ \penalty\hyphenpenalty
+ \ifdim\dimen@<1pt %
+ \underline{\hspace{\dimen@}}%
+ \else
+ \underline{\hspace{1pt}}%
+ \fi
+ \advance\dimen@ by -1pt %
+ }%
+ \underline{\hspace{1em}}%
+ \else
+ \underline{\hspace{\dimen@}}%
+ \fi
+ \endgroup
+ \xspace
+}
+\makeatother
diff --git a/macros/latex/contrib/xsim/code/xsim.collections.code.tex b/macros/latex/contrib/xsim/code/xsim.collections.code.tex
new file mode 100644
index 0000000000..62c963d631
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsim.collections.code.tex
@@ -0,0 +1,396 @@
+% ----------------------------------------------------------------------------
+% the XSIM package - collections module
+%
+% eXercise Sheets IMproved
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+\XSIMmodule{collections}{collect exercises and print collected exercises}
+
+\xsim_load_modules:n {base}
+
+\prop_new:N \g__xsim_collections_prop
+\bool_new:N \l____xsim_active_bool
+
+\msg_new:nnn {xsim} {collection-exists}
+ {
+ The~ collection~ `#1'~ you're~ trying~ to~ define~ \msg_line_context:
+ \c_space_tl already~ exists.
+ }
+
+\msg_new:nnn {xsim} {collection-active}
+ {
+ You~ can't~ print~ collection~ `#1'~ while~ it~ is~ still~ active~
+ \msg_line_context:
+ }
+
+\msg_new:nnn {xsim} {unknown-collection}
+ {
+ You're~ trying~ to~ use~ the~ collection~ `#1'~ \msg_line_context:
+ \c_space_tl but~ no~ collection~ with~ that~ name~ exists.~ You~ need~ to~
+ define~ it~ first!
+ }
+
+\msg_new:nnn {xsim} {print-collection-choice}
+ {
+ The~ choice~ `#1'~ for~ the~ option~ `print-collection/print'~ does~ not~
+ exist.~ I'm~ using~ `exercises'~ instead~ \msg_line_context:
+ }
+
+% ----------------------------------------------------------------------------
+
+% #1: collection name
+\cs_new_protected:Npn \xsim_new_collection:n #1
+ {
+ \xsim_if_collection_exists:nTF {#1}
+ { \msg_error:nnn {xsim} {collection-exists} {#1} }
+ {
+ \xsim_verbose:n { Declaring~ new~ collection~ `#1'. }
+ \prop_gput:Nnn \g__xsim_collections_prop {#1} { \c_false_bool }
+ \xsim_new_list:n {collection:#1}
+ \seq_new:c {g__xsim_collection_#1_seq}
+ }
+ }
+
+% #1: collection name
+\prg_new_conditional:Npnn \xsim_if_collection_exists:n #1 {T,F,TF}
+ {
+ \prop_if_in:NnTF \g__xsim_collections_prop {#1}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+\xsim_new_collection:n {all~ exercises}
+
+\cs_new_protected:Npn \xsim_foreach_collection:n #1
+ { \prop_map_inline:Nn \g__xsim_collections_prop {#1} }
+
+% ----------------------------------------------------------------------------
+
+% #1: collection name
+% #2: type
+\cs_new_protected:Npn \xsim_activate_collection:nn #1#2
+ {
+ \xsim_if_collection_active:nTF {#1}
+ { % TODO: error
+ }
+ {
+ \xsim_verbose:n { Activating~ collection~ `#1'. }
+ \prop_gput:Nnn \g__xsim_collections_prop {#1} { \c_true_bool }
+ \tl_if_blank:nF {#2}
+ { \seq_gput_right:cn {g__xsim_collection_#1_seq} {#2} }
+ }
+ }
+
+\cs_new_protected:Npn \xsim_activate_collection:n #1
+ { \xsim_activate_collection:nn {#1} {} }
+
+% #1: collection name
+\cs_new_protected:Npn \xsim_deactivate_collection:n #1
+ {
+ \xsim_if_collection_active:nTF {#1}
+ {
+ \xsim_verbose:n { Deactivating~ collection~ `#1'. }
+ \prop_gput:Nnn \g__xsim_collections_prop {#1} { \c_false_bool }
+ \seq_gclear:c {g__xsim_collection_#1_seq}
+ }
+ { % TODO: warning
+ }
+ }
+
+% start collection for an exercise type:
+% #1: collection name
+% #2: type
+\cs_new_protected:Npn \xsim_start_collection:nn #1#2
+ {
+ \group_begin:
+ \xsim_if_collection_exists:nF {#1}
+ { \msg_error:nnn {xsim} {unknown-collection} {#1} }
+ \xsim_activate_collection:nn {#1} {#2}
+ \xsim_setup:x { \xsim_get_parameter:nn {#2} {exercise-env} /print = false }
+ }
+\cs_generate_variant:Nn \xsim_setup:n {x}
+
+% start collection for all exercise types:
+% #1: collection name
+\cs_new_protected:Npn \xsim_start_collection:n #1
+ {
+ \group_begin:
+ \xsim_if_collection_exists:nF {#1}
+ { \msg_error:nnn {xsim} {unknown-collection} {#1} }
+ \xsim_activate_collection:n {#1}
+ \xsim_foreach_exercise_type:n
+ { \xsim_setup:x { \xsim_get_parameter:nn {##1} {exercise-env} /print = false } }
+ }
+
+% stop collection:
+% #1: collection name
+\cs_new_protected:Npn \xsim_stop_collection:n #1
+ {
+ \xsim_deactivate_collection:n {#1}
+ \group_end:
+ }
+
+% ----------------------------------------------------------------------------
+
+% #1: collection name
+% #2: type
+% #3: id
+\cs_new_protected:Npn \xsim_add_to_collection:nnn #1#2#3
+ {
+ \seq_if_empty:cTF {g__xsim_collection_#1_seq}
+ { \__xsim_add_to_collection:nnn {#1} {#2} {#3} }
+ {
+ \seq_if_in:cnT {g__xsim_collection_#1_seq} {#2}
+ { \__xsim_add_to_collection:nnn {#1} {#2} {#3} }
+ }
+ }
+
+% #1: collection name
+% #2: type
+% #3: id
+\cs_new_protected:Npn \__xsim_add_to_collection:nnn #1#2#3
+ {
+ \xsim_verbose:n
+ { Adding~ exercise~ type~ `#2'~ id~ `#3'~ to~ collection~ `#1'. }
+ \xsim_add_to_list:nn {collection:#1} {#2-#3}
+ }
+
+\cs_new:Npn \xsim_collection_count:n #1
+ { \xsim_list_count:n {collection:#1} }
+
+% #1: type
+% #2: id
+\cs_new_protected:Npn \xsim_update_collections:nn #1#2
+ {
+ \xsim_if_insert_mode:F
+ {
+ \xsim_exercise_if_use:nnT {#1} {#2}
+ { \xsim_add_to_collection:nnn {all~ exercises} {#1} {#2} }
+ \xsim_foreach_collection:n
+ {
+ \xsim_exercise_if_use:nnTF {#1} {#2}
+ {
+ \xsim_if_collection_active:nT {##1}
+ { \xsim_add_to_collection:nnn {##1} {#1} {#2} }
+ }
+ { \xsim_remove_from_list:nn {collection:##1} {#1-#2} }
+ }
+ }
+ }
+\cs_generate_variant:Nn \xsim_update_collections:nn {nV}
+
+% #1: collection name
+\cs_new_protected:Npn \xsim_get_collections:
+ {
+ \xsim_foreach_collection:n
+ {
+ \xsim_foreach_list_entry:nn {collection:##1}
+ { \__xsim_list_to_collection:nww {##1} ####1 \q_stop }
+ }
+ }
+
+\cs_new_protected:Npn \__xsim_list_to_collection:nww #1#2-#3 \q_stop
+ { \xsim_add_to_collection:nnn {#1} {#2} {#3} }
+
+% ----------------------------------------------------------------------------
+
+% #1: collection name
+% #2: type
+% #3: id
+\prg_new_conditional:Npnn \xsim_if_in_collection:nnn #1#2#3 {T,F,TF}
+ {
+ \xsim_if_in_list:nnTF {collection:#1} {#2-#3}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\cs_generate_variant:Nn \xsim_if_in_collection:nnnTF {V}
+\cs_generate_variant:Nn \xsim_if_in_collection:nnnT {x,nV}
+
+% ----------------------------------------------------------------------------
+
+\bool_new:N \l__xsim_printcollection_headings_bool
+\tl_new:N \l__xsim_printcollection_headings_template_tl
+\tl_new:N \l__xsim_print_collection_choice_tl
+
+\keys_define:nn {xsim/print-collection}
+ {
+ headings .bool_set:N = \l__xsim_printcollection_headings_bool ,
+ headings .initial:n = false ,
+ headings-template .tl_set:N = \l__xsim_printcollection_headings_template_tl ,
+ headings-template .initial:n = collection ,
+ print .choice: ,
+ print/exercises .code:n =
+ \tl_set:Nn \l__xsim_print_collection_choice_tl {exercise} ,
+ print/solutions .code:n =
+ \tl_set:Nn \l__xsim_print_collection_choice_tl {solution} ,
+ print/both .code:n =
+ \tl_set:Nn \l__xsim_print_collection_choice_tl {both} ,
+ print/unknown .code:n =
+ \msg_warning:nnn {xsim} {print-collection-choice} {#1}
+ \tl_set:Nn \l__xsim_print_collection_choice_tl {exercise} ,
+ print .initial:n = exercises
+ }
+
+% #1: options
+% #2: collection name
+\cs_new_protected:Npn \xsim_print_collection:nn #1#2
+ {
+ \xsim_if_collection_active:nTF {#2}
+ { \msg_error:nnn {xsim} {collection-active} {#2} }
+ {
+ \xsim_verbose:x
+ {
+ Printing~
+ \l__xsim_print_collection_choice_tl \c_space_tl
+ \str_if_eq:onT { \l__xsim_print_collection_choice_tl } {both}
+ { exercises~ and~ solutions~ }
+ of~ collection~ `#2'
+ }
+ \group_begin:
+ \keys_set:nn {xsim/print-collection} {#1}
+ \tl_set:Nn \ExerciseCollection {#2}
+ \xsim_foreach_exercise_type:n
+ {
+ \tl_set:Nn \ExerciseType {##1}
+ \bool_if:NT \l__xsim_printcollection_headings_bool
+ {
+ \bool_set_false:N \l__xsim_tmpa_bool
+ \xsim_foreach_exercise_id:n
+ {
+ \xsim_if_in_collection:nnnT {#2} {##1} {####1}
+ {
+ \tl_if_eq:VnTF
+ \l__xsim_print_collection_choice_tl
+ {both}
+ {
+ \xsim_if_exercise_exists:nnT {##1} {####1}
+ { \bool_set_true:N \l__xsim_tmpa_bool }
+ }
+ {
+ \use:c
+ {
+ xsim_if_
+ \l__xsim_print_collection_choice_tl
+ _exists:nnT
+ }
+ {##1}
+ {####1}
+ { \bool_set_true:N \l__xsim_tmpa_bool }
+ }
+ }
+ }
+ \bool_if:NT \l__xsim_tmpa_bool
+ {
+ \xsim_use_template:nV
+ {heading}
+ \l__xsim_printcollection_headings_template_tl
+ }
+ }
+ \xsim_foreach_exercise_id:n
+ {
+ \xsim_if_in_collection:nnnT {#2} {##1} {####1}
+ {
+ \tl_if_eq:VnTF
+ \l__xsim_print_collection_choice_tl
+ {both}
+ {
+ \xsim_if_exercise_exists:nnT {##1} {####1}
+ { \xsim_insert:nnnn {##1} {####1} {} {exercise} }
+ \xsim_if_solution_exists:nnT {##1} {####1}
+ { \xsim_insert:nnnn {##1} {####1} {} {solution} }
+ }
+ {
+ \use:c
+ {
+ xsim_if_
+ \l__xsim_print_collection_choice_tl
+ _exists:nnT
+ }
+ {##1}
+ {####1}
+ {
+ \xsim_insert:nnnV
+ {##1}
+ {####1}
+ {}
+ \l__xsim_print_collection_choice_tl
+ }
+ }
+ }
+ }
+ }
+ \group_end:
+ }
+ }
+
+% ----------------------------------------------------------------------------
+
+% #1: collection name
+\prg_new_conditional:Npnn \xsim_if_collection_active:n #1 {T,F,TF}
+ {
+ \prop_if_in:NnTF \g__xsim_collections_prop {#1}
+ {
+ \bool_if:nTF
+ {
+ !\str_if_eq_x_p:nn {#1} {all~ exercises} &&
+ \prop_item:Nn \g__xsim_collections_prop {#1}
+ }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+ { \prg_return_false: }
+ }
+
+\prg_new_protected_conditional:Npnn \xsim_if_collections_active: {T,F,TF}
+ {
+ \bool_set_false:N \l____xsim_active_bool
+ \prop_map_inline:Nn \g__xsim_collections_prop
+ {
+ \xsim_if_collection_active:nT {##1}
+ {
+ \bool_set_true:N \l____xsim_active_bool
+ \prop_map_break:
+ }
+ }
+ \bool_if:NTF \l____xsim_active_bool
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+% ----------------------------------------------------------------------------
+
+\AtBeginDocument
+ {
+ \xsim_get_collections:
+ \xsim_activate_collection:n {all~ exercises}
+ }
+
+\AtEndDocument
+ {
+ \xsim_deactivate_collection:n {all~ exercises}
+ \xsim_foreach_collection:n { \xsim_update_list:n {collection:#1} }
+ }
+
+% ----------------------------------------------------------------------------
+\file_input_stop:
diff --git a/macros/latex/contrib/xsim/code/xsim.definitions.code.tex b/macros/latex/contrib/xsim/code/xsim.definitions.code.tex
new file mode 100644
index 0000000000..1c27ad960a
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsim.definitions.code.tex
@@ -0,0 +1,431 @@
+% ----------------------------------------------------------------------------
+% the XSIM package - definitions module
+%
+% eXercise Sheets IMproved
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+\XSIMmodule{definitions}{definition of user commands}
+
+\xsim_load_modules:n {base,exercises,blanks,translations,interface,grades,random}
+
+% not exactly a user choice: the `exercises' environments rely on them:
+\DeclareExerciseParameter*!{exercise-env}
+\DeclareExerciseParameter*!{solution-env}
+\DeclareExerciseParameter !{exercise-name}
+\DeclareExerciseParameter !{solution-name}
+\DeclareExerciseParameter !{exercise-template}
+\DeclareExerciseParameter !{solution-template}
+\DeclareExerciseParameter* {counter}
+\DeclareExerciseParameter* {solution-counter}
+\DeclareExerciseParameter* {number}
+
+% ----------------------------------------------------------------------------
+
+% not exactly a user choice, must be present at various places:
+\DeclareExerciseProperty! {id}
+\DeclareExerciseProperty! {ID}
+\DeclareExerciseProperty {counter}
+\DeclareExerciseProperty {counter-value}
+\DeclareExerciseProperty * {print}
+\DeclareExerciseProperty *-{print!}
+\DeclareExerciseProperty * {use}
+\DeclareExerciseProperty * {used}
+\DeclareExerciseProperty *-{use!}
+\XSIMifchapterT{
+ \DeclareExerciseProperty {chapter-value}
+ \DeclareExerciseProperty {chapter}
+}
+\DeclareExerciseProperty {section-value}
+\DeclareExerciseProperty {section}
+\DeclareExerciseProperty {sectioning}
+
+% those are additional and wouldn't *need* to be present except for
+% pre-defined templates which make use of them:
+\DeclareExerciseProperty {subtitle}
+\DeclareExerciseProperty {points}
+\DeclareExerciseProperty {bonus-points}
+\DeclareExerciseProperty {page-value}
+\DeclareExerciseProperty {page}
+
+\DeclareExercisePropertyAlias {ID} {id}
+
+% ----------------------------------------------------------------------------
+
+\DeclareExerciseTagging {tags}
+\DeclareExerciseTagging {topics}
+
+\xsimsetup{tags/ingore-untagged=false}
+
+% ----------------------------------------------------------------------------
+
+\DeclareExerciseGoal {points}
+\DeclareExerciseGoal {bonus-points}
+
+\NewDocumentCommand \printgoal {m}
+ { \ExerciseGoalValuePrint {#1} {} {} }
+
+\NewDocumentCommand \points {m}
+ {
+ \ExerciseGoalValuePrint {#1}
+ { \, \XSIMtranslate {point} }
+ { \, \XSIMtranslate {points} }
+ }
+
+\NewDocumentCommand \addpoints {sm}
+ {
+ \AddtoExerciseGoal {points} {#2}
+ \IfBooleanF {#1} { \points {#2} }
+ }
+
+\NewDocumentCommand \printpoints {m}
+ {
+ \TotalExerciseTypeGoal {#1} {points}
+ { \, \XSIMtranslate {point} }
+ { \, \XSIMtranslate {points} }
+ }
+
+\NewDocumentCommand \printtotalpoints {}
+ {
+ \TotalExerciseGoal {points}
+ { \, \XSIMtranslate {point} }
+ { \, \XSIMtranslate {points} }
+ }
+
+\NewDocumentCommand \addbonus {sm}
+ {
+ \AddtoExerciseGoal {bonus-points} {#2}
+ \IfBooleanF {#1} { \points {#2} }
+ }
+
+\NewDocumentCommand \printbonus {m}
+ {
+ \TotalExerciseTypeGoal {#1} {bonus-points}
+ { \, \XSIMtranslate {point} }
+ { \, \XSIMtranslate {points} }
+ }
+
+\NewDocumentCommand \printtotalbonus {}
+ {
+ \TotalExerciseGoal {bonus-points}
+ { \, \XSIMtranslate {point} }
+ { \, \XSIMtranslate {points} }
+ }
+
+% ----------------------------------------------------------------------------
+\NewDocumentCommand \pointsforgrade {m}
+ {
+ \GetGradeRequirementForGoal {#1} {points}
+ { \, \XSIMtranslate {point} }
+ { \, \XSIMtranslate {points} }
+ }
+
+\NewDocumentCommand \goalsforgrade {mm}
+ {
+ \GetGradeRequirementForGoals {#2} {#1}
+ { \, \XSIMtranslate {point} }
+ { \, \XSIMtranslate {points} }
+ }
+
+\NewDocumentCommand \totalgoalforgrade {m}
+ {
+ \GetGradeRequirement {#1}
+ { \, \XSIMtranslate {point} }
+ { \, \XSIMtranslate {points} }
+ }
+
+% ----------------------------------------------------------------------------
+
+\DeclareExerciseType {exercise} {
+ exercise-env = exercise ,
+ solution-env = solution ,
+ exercise-name = \XSIMtranslate {exercise} ,
+ solution-name = \XSIMtranslate {solution} ,
+ exercise-template = default ,
+ solution-template = default
+}
+
+% ----------------------------------------------------------------------------
+
+\DeclareExerciseEnvironmentTemplate {default}
+ {
+ \subsection*
+ {
+ \XSIMmixedcase { \GetExerciseName } \nobreakspace
+ \GetExerciseProperty {counter}
+ \IfInsideSolutionF
+ {
+ \IfExercisePropertySetT {subtitle}
+ { ~ { \normalfont \itshape \GetExerciseProperty {subtitle} } }
+ }
+ }
+ \GetExercisePropertyT {points}
+ {
+ \marginpar
+ {
+ \IfInsideSolutionF { \rule {1.2cm} {1pt} \slash }
+ \printgoal {\PropertyValue}
+ \GetExercisePropertyT {bonus-points}
+ { \nobreakspace ( + \printgoal {\PropertyValue} ) }
+ \nobreakspace\XSIMtranslate {point-abbr}
+ }
+ }
+ }
+ {}
+
+% ----------------------------------------------------------------------------
+
+\DeclareExerciseHeadingTemplate {none} {}
+
+\DeclareExerciseHeadingTemplate {default}
+ { \section* { \XSIMtranslate {default-heading} } }
+
+\DeclareExerciseHeadingTemplate {collection}
+ { \section* { \XSIMtranslate {collection-heading} } }
+
+\DeclareExerciseHeadingTemplate {per-section}
+ { \section* { \XSIMtranslate {per-section-heading} } }
+
+\DeclareExerciseHeadingTemplate {per-chapter}
+ { \section* { \XSIMtranslate {per-chapter-heading} } }
+
+% ----------------------------------------------------------------------------
+
+\RequirePackage{array,booktabs}
+
+\DeclareExerciseTableTemplate {default*}
+ {
+ \XSIMputright \ExerciseTableCode
+ {
+ \toprule
+ \XSIMifblankTF {\ExerciseType}
+ {}
+ { \XSIMmixedcase { \GetExerciseParameter {exercise-name} } }
+ &
+ }
+ \ForEachUsedExerciseByType
+ {
+ \XSIMifeqTF {#1} { \ExerciseTableType {#1} }
+ {
+ \XSIMifblankTF { \ExerciseType }
+ {
+ \XSIMputright \ExerciseTableCode
+ {
+ \XSIMmixedcase
+ { \ExerciseParameterGet {#1} {exercise-name} ~ }
+ }
+ }
+ {}
+ \XSIMputright \ExerciseTableCode { #3 & }
+ }
+ {}
+ }
+ \XSIMputright \ExerciseTableCode
+ {
+ \XSIMtranslate {total} \\
+ \midrule \XSIMmixedcase { \XSIMtranslate {points} } &
+ }
+ \ForEachUsedExerciseByType
+ {
+ \XSIMifeqTF {#1} { \ExerciseTableType {#1} }
+ {
+ \XSIMputright \ExerciseTableCode
+ { \XSIMifblankTF {#5} {\printgoal{0}} {\printgoal{#5}} & }
+ }
+ {}
+ }
+ \XSIMputright \ExerciseTableCode
+ {
+ \XSIMifblankTF {\ExerciseType}
+ { \TotalExerciseGoal {points} {} {} }
+ { \TotalExerciseTypeGoal {\ExerciseType} {points} {} {} }
+ \\ \midrule
+ \XSIMtranslate {reached} &
+ }
+ \ForEachUsedExerciseByType
+ {
+ \XSIMifeqTF {#1} { \ExerciseTableType {#1} }
+ { \XSIMputright \ExerciseTableCode {&} }
+ {}
+ }
+ \XSIMputright \ExerciseTableCode { \\ \bottomrule }
+ \def\numberofcolumns{
+ \XSIMifblankTF {\ExerciseType}
+ {\numberofusedexercises}
+ {\csname numberof \ExerciseType s\endcsname}
+ }
+ \XSIMifeqF{\numberofcolumns}{0}
+ {
+ \begin {tabular} {l*{\numberofcolumns}{c}c}
+ \ExerciseTableCode
+ \end {tabular}
+ }
+ }
+
+\DeclareExerciseTableTemplate {default}
+ {
+ \XSIMputright \ExerciseTableCode
+ {
+ \toprule
+ \XSIMifblankTF {\ExerciseType}
+ {}
+ { \XSIMmixedcase { \GetExerciseParameter {exercise-name} } }
+ &
+ \XSIMmixedcase { \XSIMtranslate {points} } &
+ \XSIMtranslate {reached} \\
+ \midrule
+ }
+ \ForEachUsedExerciseByType
+ {
+ \XSIMifeqTF {#1} { \ExerciseTableType {#1} }
+ {
+ \XSIMifblankTF { \ExerciseType }
+ {
+ \XSIMputright \ExerciseTableCode
+ {
+ \XSIMmixedcase
+ { \ExerciseParameterGet {#1} {exercise-name} ~ }
+ }
+ }
+ {}
+ \XSIMputright \ExerciseTableCode
+ { #3 & \XSIMifblankTF {#5} {\printgoal{0}} {\printgoal{#5}} & \\ }
+ }
+ {}
+ }
+ \XSIMputright \ExerciseTableCode
+ {
+ \midrule
+ \XSIMtranslate {total} &
+ \XSIMifblankTF {\ExerciseType}
+ { \TotalExerciseGoal {points} {} {} }
+ { \TotalExerciseTypeGoal {\ExerciseType} {points} {} {} } &
+ \\ \bottomrule
+ }
+ \XSIMexpandcode
+ {
+ \noexpand \begin {tabular} {\XSIMifblankTF{\ExerciseType}{l}{c}cc}
+ \noexpand \ExerciseTableCode
+ \noexpand \end {tabular}
+ }
+ }
+
+% ----------------------------------------------------------------------------
+
+\DeclareExerciseTranslations {exercise} {
+ Fallback = exercise ,
+ English = exercise ,
+ French = exercice ,
+ German = \"Ubung
+}
+
+\DeclareExerciseTranslations {question} {
+ Fallback = question ,
+ English = question ,
+ French = question ,
+ German = Aufgabe
+}
+
+\DeclareExerciseTranslations {solution} {
+ Fallback = solution ,
+ English = solution ,
+ French = solution ,
+ German = L\"osung
+}
+
+\DeclareExerciseTranslations {point-abbr} {
+ Fallback = p. ,
+ English = p. ,
+ French = p. ,
+ German = P.
+}
+
+\DeclareExerciseTranslations {point} {
+ Fallback = point ,
+ English = point ,
+ French = point ,
+ German = Punkt
+}
+
+\DeclareExerciseTranslations {points} {
+ Fallback = points ,
+ English = points ,
+ French = points ,
+ German = Punkte
+}
+
+\DeclareExerciseTranslations {reached} {
+ Fallback = reached ,
+ English = reached ,
+ French = atteint ,
+ German = erreicht
+}
+
+\DeclareExerciseTranslations {total} {
+ Fallback = total ,
+ English = total ,
+ French = totalement ,
+ German = insgesamt
+}
+
+\DeclareExerciseTranslations {default-heading} {
+ Fallback = \XSIMmixedcase { \GetExerciseParameter {solution-name} s }~
+ to~ the~ \XSIMmixedcase { \GetExerciseParameter {exercise-name} s} ,
+ English = \XSIMmixedcase { \GetExerciseParameter {solution-name} s }~
+ to~ the~ \XSIMmixedcase { \GetExerciseParameter {exercise-name} s} ,
+ German = \XSIMmixedcase { \GetExerciseParameter {solution-name} en }~
+ zu~ den~ \XSIMmixedcase { \GetExerciseParameter {exercise-name} en}
+}
+
+\DeclareExerciseTranslations {collection-heading} {
+ Fallback = \XSIMmixedcase { \GetExerciseParameter {exercise-name} s } ,
+ English = \XSIMmixedcase { \GetExerciseParameter {exercise-name} s } ,
+ German = \XSIMmixedcase { \GetExerciseParameter {exercise-name} en }
+}
+
+\DeclareExerciseTranslations {per-section-heading} {
+ Fallback = \XSIMmixedcase { \GetExerciseParameter {solution-name} s }~
+ to~ the~ \XSIMmixedcase { \GetExerciseParameter {exercise-name} s}~
+ of~ Section \nobreakspace \ExerciseSection ,
+ English = \XSIMmixedcase { \GetExerciseParameter {solution-name} s }~
+ to~ the~ \XSIMmixedcase { \GetExerciseParameter {exercise-name} s}~
+ of~ Section \nobreakspace \ExerciseSection ,
+ German = \XSIMmixedcase { \GetExerciseParameter {solution-name} en }~
+ zu~ den~ \XSIMmixedcase { \GetExerciseParameter {exercise-name} en}~
+ in~ Abschnitt \nobreakspace \ExerciseSection
+}
+
+\DeclareExerciseTranslations {per-chapter-heading} {
+ Fallback = \XSIMmixedcase { \GetExerciseParameter {solution-name} s }~
+ to~ the~ \XSIMmixedcase { \GetExerciseParameter {exercise-name} s}~
+ of~ Chapter \nobreakspace \ExerciseChapter ,
+ English = \XSIMmixedcase { \GetExerciseParameter {solution-name} s }~
+ to~ the~ \XSIMmixedcase { \GetExerciseParameter {exercise-name} s}~
+ of~ Chapter \nobreakspace \ExerciseChapter ,
+ German = \XSIMmixedcase { \GetExerciseParameter {solution-name} en }~
+ zu~ den~ \XSIMmixedcase { \GetExerciseParameter {exercise-name} en}~
+ in~ Kapitel \nobreakspace \ExerciseChapter
+}
+
+% ----------------------------------------------------------------------------
+\file_input_stop:
diff --git a/macros/latex/contrib/xsim/code/xsim.environments.code.tex b/macros/latex/contrib/xsim/code/xsim.environments.code.tex
new file mode 100644
index 0000000000..ac12cb5153
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsim.environments.code.tex
@@ -0,0 +1,380 @@
+% ----------------------------------------------------------------------------
+% the XSIM package - environments module
+%
+% eXercise Sheets IMproved
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+\XSIMmodule{environments}{generic code for exercise and solution environments}
+
+\xsim_load_modules:n {templates,verbwrite}
+
+\tl_new:N \l__xsim_file_name_signature_tl
+\tl_new:N \l__xsim_file_path_tl
+\tl_new:N \l__xsim_file_path_and_name_tl
+\tl_new:N \l__xsim_file_name_tl
+\tl_new:N \l__xsim_file_extension_tl
+\bool_new:N \l__xsim_options_given_bool
+\bool_new:N \l__xsim_insert_mode_bool
+
+\tl_new:N \ExerciseText
+\tl_set:Nn \ExerciseText { \msg_error:nn {xsim} {only-in-solution} }
+
+% ----------------------------------------------------------------------------
+
+\msg_new:nnn {xsim} {only-in-solution}
+ {
+ You~ can't~ use~ \token_to_str:N \ExerciseText \c_space_tl inside~ an~
+ exercise.~ It~ is~ only~ allowed~ inside~ solution~ environments!
+ }
+
+% ----------------------------------------------------------------------------
+
+\keys_define:nn {xsim}
+ {
+ path .tl_set:N = \l__xsim_file_path_tl ,
+ file-extension .tl_set:N = \l__xsim_file_extension_tl ,
+ file-extension .initial:n = tex
+ }
+
+% ----------------------------------------------------------------------------
+
+\tl_const:Nn \c__xsim_comment_line_tl { \c__xsim_percent_char_tl }
+\tl_const:Nn \c__xsim_comment_line_fill_tl { \prg_replicate:nn {72} {-} }
+\tl_const:Nn \c__xsim_comment_line_indent_tl { \c_space_tl \c_space_tl }
+
+\cs_new:Npn \__xsim_comment_line_and_feed:n #1
+ { \__xsim_comment_line:n {#1} ^^J }
+
+\cs_new:Npn \__xsim_comment_line:n #1
+ {
+ \c__xsim_comment_line_tl
+ \tl_if_blank:nF {#1} { \c_space_tl }
+ #1
+ }
+
+\cs_new:Npn \__xsim_two_digits:n #1
+ { \int_compare:nT { #1 < 10 } {0} \int_eval:n {#1} }
+
+% #1: type
+% #2: id
+% #3: exercise|solution
+\cs_new_protected:Npn \__xsim_set_file_signature:nnn #1#2#3
+ { \tl_set:Nn \l__xsim_file_name_signature_tl {#1-#2-#3} }
+\cs_generate_variant:Nn \__xsim_set_file_signature:nnn {nx,nV}
+
+\tl_set:Nn \l__xsim_file_name_tl
+ { \c_sys_jobname_str - \l__xsim_file_name_signature_tl -body . \l__xsim_file_extension_tl }
+
+\tl_set:Nn \l__xsim_file_path_and_name_tl
+ {
+ \tl_if_blank:VF \l__xsim_file_path_tl
+ { \l__xsim_file_path_tl / }
+ \l__xsim_file_name_tl
+ }
+
+% #1: type
+% #2: id
+% #3: exercise|solution
+% #4: file name
+% #5: path
+\cs_new_protected:Npn \__xsim_file_info:nnnnn #1#2#3#4#5
+ {
+ \tl_set:Nn \l_xsim_file_begin_tl
+ {
+ \__xsim_comment_line_and_feed:n { \c__xsim_comment_line_fill_tl }
+ \__xsim_comment_line_and_feed:n {file~ `#4'}
+ \tl_if_blank:nF {#5}
+ {
+ \__xsim_comment_line_and_feed:n
+ { \c__xsim_comment_line_indent_tl in~ folder~ `#5/'}
+ }
+ \__xsim_comment_line_and_feed:n {}
+ \__xsim_comment_line_and_feed:n
+ {
+ \c__xsim_comment_line_indent_tl
+ \c__xsim_comment_line_indent_tl
+ #3~ of~ type~ `#1'~ with~ id~ `#2'
+ }
+ \__xsim_comment_line_and_feed:n {}
+ \__xsim_comment_line_and_feed:n
+ {generated~ by~ the~ `\@currenvir'~ environment~ of~ the}
+ \__xsim_comment_line_and_feed:n
+ {
+ \c__xsim_comment_line_indent_tl
+ `xsim'~ package~ v \c_xsim_version_tl
+ \c_space_tl (\c_xsim_date_tl)
+ }
+ \__xsim_comment_line_and_feed:n
+ {
+ from~ source~ `\c_sys_jobname_str'~ on~
+ \int_use:N \c_sys_year_int /
+ \__xsim_two_digits:n { \c_sys_month_int } /
+ \__xsim_two_digits:n { \c_sys_day_int } ~
+ \msg_line_context:
+ }
+ \__xsim_comment_line:n { \c__xsim_comment_line_fill_tl }
+ }
+ }
+\cs_generate_variant:Nn \__xsim_file_info:nnnnn {nnnVV}
+
+% ----------------------------------------------------------------------------
+
+% #1: type
+% #2: id
+% #3: exercise|solution
+\cs_new_protected:Npn \xsim_save_environment_body:nnn #1#2#3
+ {
+ \xsim_verbose:n
+ { Starting~ to~ write~ #3~ environment~ type~ `#1'~ id~ `#2' }
+ \__xsim_set_file_signature:nnn {#1} {#2} {#3}
+ \__xsim_file_info:nnnVV {#1} {#2} {#3}
+ \l__xsim_file_name_tl
+ \l__xsim_file_path_tl
+ \int_zero:N \l_xsim_line_gobble_int
+ \xsim_file_write_start:nV
+ { \l__xsim_options_given_bool }
+ \l__xsim_file_path_and_name_tl
+ }
+
+\cs_new_protected:Npn \xsim_save_environment_body_end:
+ { \xsim_file_write_stop: }
+
+% ----------------------------------------------------------------------------
+% #1: type
+% #2: exercise|solution
+% #3: hook
+\cs_new_protected:Npn \xsim_new_hook:nnn #1#2#3
+ {
+ \xsim_verbose:n { Defining~ new~ #1~ (#2)~ hook~ `#3' }
+ \tl_new:c {l__xsim_#1_#2_#3_hook_tl}
+ \keys_define:nx {xsim}
+ {
+ \xsim_get_parameter:nn {#1} {#2-env}/#3-hook .tl_set:N =
+ \exp_not:c {l__xsim_#1_#2_#3_hook_tl}
+ }
+ }
+
+% #1: type
+% #2: exercise|solution
+% #3: hook
+\cs_new:Npn \xsim_use_hook:nnn #1#2#3
+ { \exp_not:v {l__xsim_#1_#2_#3_hook_tl} }
+
+% #1: type
+% #2: exercise|solution
+% #3: hook
+% #4: code
+\cs_new_protected:Npn \xsim_set_hook:nnnn #1#2#3#4
+ {
+ \xsim_verbose:n { Setting~ #1~ (#2)~ hook~ `#3' }
+ \tl_set:cn {l__xsim_#1_#2_#3_hook_tl} {#4}
+ }
+
+% #1: type
+% #2: exercise|solution
+% #3: hook
+% #4: code
+\cs_new_protected:Npn \xsim_addto_hook:nnnn #1#2#3#4
+ {
+ \xsim_verbose:n { Adding~ to~ #1~ (#2)~ hook~ `#3' }
+ \tl_put_right:cn {l__xsim_#1_#2_#3_hook_tl} {#4}
+ }
+
+% ----------------------------------------------------------------------------
+
+% #1: type
+% #2: id
+% #3: exercise|solution
+\cs_new_protected:Npn \xsim_start_environment:nnn #1#2#3
+ { \xsim_save_environment_body:nnn {#1} {#2} {#3} }
+\cs_generate_variant:Nn \xsim_start_environment:nnn {nV}
+
+% #1: type
+% #2: id
+% #3: exercise|solution
+\cs_new_protected:Npn \xsim_stop_environment:nnn #1#2#3
+ {
+ \xsim_save_environment_body_end:
+ \xsim_if_print:nnnTF {#1} {#2} {#3}
+ { \xsim_typeset_environment:nnn {#1} {#2} {#3} }
+ { \xsim_use_hook:nnn {#1} {#3} {print-false} }
+ }
+\cs_generate_variant:Nn \xsim_stop_environment:nnn {nV}
+
+% #1: type
+% #2: id
+% #3: exercise|solution
+\cs_new_protected:Npn \xsim_typeset_environment:nnn #1#2#3
+ {
+ \xsim_verbose:n
+ { Typesetting~ #3~ environment~ type~ `#1'~ id~ `#2' }
+ \use:x
+ {
+ \exp_not:N \par
+ \xsim_use_hook:nnn {#1} {#3} {pre}
+ \xsim_use_template:nn
+ {begin}
+ { \xsim_get_parameter:nn {#1} {#3-template} }
+ \xsim_use_hook:nnn {#1} {#3} {begin}
+ \file_if_exist:nTF { \exp_not:V \l__xsim_file_path_and_name_tl }
+ { \file_input:n { \exp_not:V \l__xsim_file_path_and_name_tl } }
+ {
+ \xsim_verbose:n
+ { File~ `\l__xsim_file_path_and_name_tl'~ doesn't~ exist. }
+ }
+ \xsim_use_hook:nnn {#1} {#3} {end}
+ \xsim_use_template:nn
+ {end}
+ { \xsim_get_parameter:nn {#1} {#3-template} }
+ \xsim_use_hook:nnn {#1} {#3} {post}
+ \exp_not:N \par
+ }
+ }
+
+% #1: type
+% #2: id
+% #3: options
+% #4: exercise|solution
+\cs_new_protected:Npn \xsim_insert:nnnn #1#2#3#4
+ {
+ \__xsim_set_file_signature:nnn {#1} {#2} {#4}
+ \file_if_exist:VTF \l__xsim_file_path_and_name_tl
+ {
+ \xsim_verbose:n
+ { Inserting~ #4~ type~ `#1'~ id~ `#2'. }
+ \group_begin:
+ \xsim_insert_mode:
+ \use:c {xsim_start_#4:nn} {#1} {#3}
+ \tl_set:Nn \ExerciseType {#1}
+ \tl_set:Nn \ExerciseID {#2}
+ \str_if_eq:nnT {#4} {exercise}
+ {
+ \xsim_if_boolean_property:nnnF {#1} {#2} {used}
+ {
+ \xsim_step_exercise_counter:nn {#1} {#2}
+ \xsim_set_properties:nnx {#1} {#2}
+ {
+ page-value = \arabic{page} ,
+ page = \thepage ,
+ section-value = \arabic{section} ,
+ section = \thesection
+ }
+ }
+ \xsim_if_property_exist:nT {chapter}
+ {
+ \xsim_set_properties:nnx {#1} {#2}
+ {
+ chapter-value = \arabic{chapter} ,
+ chapter = \thechapter
+ }
+ }
+ \xsim_set_properties:nnn {#1} {#2}
+ { print=true , used=true }
+ }
+ \str_if_eq:nnT {#4} {solution}
+ {
+ \__xsim_set_file_signature:nnn {#1} {#2} {exercise}
+ \tl_set_from_file:Nnn \ExerciseText {}
+ {\l__xsim_file_path_and_name_tl}
+ \tl_trim_spaces:N \ExerciseText
+ }
+ \use:x
+ {
+ \xsim_use_hook:nnn {#1} {#4} {pre}
+ \xsim_use_template:nn
+ {begin}
+ { \xsim_get_parameter:nn {#1} {#4-template} }
+ \xsim_use_hook:nnn {#1} {#4} {begin}
+ \xsim_input:nnn {#1} {#2} {#4}
+ \xsim_use_hook:nnn {#1} {#4} {end}
+ \xsim_use_template:nn
+ {end}
+ { \xsim_get_parameter:nn {#1} {#4-template} }
+ \xsim_use_hook:nnn {#1} {#4} {post}
+ }
+ \group_end:
+ }
+ {
+ \xsim_verbose:x
+ { File~ `\l__xsim_file_path_and_name_tl'~ doesn't~ exist. }
+ }
+ }
+\cs_generate_variant:Nn \xsim_insert:nnnn {nV,VV,nnnV}
+
+% #1: type
+% #2: id
+% #3: exercise|solution
+\cs_new_protected:Npn \xsim_input:nnn #1#2#3
+ {
+ \group_begin:
+ \__xsim_set_file_signature:nnn {#1} {#2} {#3}
+ \file_input:n { \l__xsim_file_path_and_name_tl }
+ \group_end:
+ }
+\cs_generate_variant:Nn \xsim_input:nnn {oo}
+
+\prg_new_conditional:Npnn \xsim_if_insert_mode: {T,F,TF}
+ {
+ \bool_if:NTF \l__xsim_insert_mode_bool
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+\cs_new_protected:Npn \xsim_insert_mode:
+ { \bool_set_true:N \l__xsim_insert_mode_bool }
+
+% ----------------------------------------------------------------------------
+
+% #1: type
+% #2: exercise|solution
+\cs_new_protected:Npn \xsim_new_environment:nn #1#2
+ {
+ \xsim_verbose:n { Defining~ #2~ environment~ type~ `#1'. }
+ \xsim_new_hook:nnn {#1} {#2} {pre}
+ \xsim_new_hook:nnn {#1} {#2} {begin}
+ \xsim_new_hook:nnn {#1} {#2} {end}
+ \xsim_new_hook:nnn {#1} {#2} {post}
+ \xsim_new_hook:nnn {#1} {#2} {print-false}
+ \NewDocumentEnvironment
+ { \xsim_get_parameter:nn {#1} {#2-env} } {o}
+ {
+ \IfNoValueTF {##1}
+ {
+ \bool_set_false:N \l__xsim_options_given_bool
+ \use:c {xsim_start_#2:nn} {#1} {}
+ }
+ {
+ \bool_set_true:N \l__xsim_options_given_bool
+ \use:c {xsim_start_#2:nn} {#1} {##1}
+ }
+ \xsim_start_environment:nVn {#1} \g_xsim_exercise_id_tl {#2}
+ }
+ {
+ \xsim_stop_environment:nVn {#1} \g_xsim_exercise_id_tl {#2}
+ \use:c {xsim_stop_#2:n} {#1}
+ }
+ }
+
+% ----------------------------------------------------------------------------
+\file_input_stop:
diff --git a/macros/latex/contrib/xsim/code/xsim.exercises.code.tex b/macros/latex/contrib/xsim/code/xsim.exercises.code.tex
new file mode 100644
index 0000000000..a269fe2c5d
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsim.exercises.code.tex
@@ -0,0 +1,556 @@
+% ----------------------------------------------------------------------------
+% the XSIM package - exercises module
+%
+% eXercise Sheets IMproved
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+\XSIMmodule{exercises}{main exercises definitions}
+
+\xsim_load_modules:n
+ {
+ base, properties, environments, templates, solutions, tags, goals,
+ collections
+ }
+
+\seq_new:N \l__xsim_parameters_seq
+\seq_new:N \l__xsim_fixed_parameters_seq
+\seq_new:N \l__xsim_mandatory_parameters_seq
+\prop_new:N \g__xsim_exercises_prop
+\int_gzero_new:N \g_xsim_id_int
+\int_gzero_new:N \g_xsim_max_id_int
+\tl_new:N \l_xsim_current_id_tl
+\tl_new:N \g_xsim_exercise_id_tl
+\tl_new:N \ExerciseID
+\bool_new:N \l____xsim_print_bool
+
+% ----------------------------------------------------------------------------
+
+\msg_new:nnn {xsim} {parameter-fixed}
+ {
+ You~ tried~ to~ set~ parameter~ `#2'~ for~ type~ `#1'~ \msg_line_context:
+ . ~ However,~ parameter~ `#2'~ is~ a~ fixed~ parameter~ that~ already~
+ has~ been~ set~ for~ type~ `#1'.~ Its~ value~ can't~ be~ changed!
+ }
+
+\msg_new:nnn {xsim} {type-exists}
+ {
+ You~ try~ to~ define~ exercise~ type~ `#1'~ \msg_line_context: .~
+ However,~ the~ exercise~ type~ `#1'~ already~ exists.
+ }
+
+\msg_new:nnn {xsim} {mandatory-parameter}
+ {
+ You~ forget~ to~ set~ the~ mandatory~ parameter~ `#1'~ when~ defining~
+ exercise~ type~ `#2'.
+ }
+
+\msg_new:nnn {xsim} {unknown-parameter}
+ {
+ You~ tried~ to~ retrieve~ the~ parameter~ `#1'~ \msg_line_context: . \\
+ This~ parameter~ does~ not~ exist.~ Check~ for~ a~ typo~ or~ \\
+ define~ the~ parameter~ yourself.
+ }
+
+% ----------------------------------------------------------------------------
+
+\xsim_new_list:n {types}
+
+% #1: true|false (fixed parameter)
+% #2: true|false (mandatory parameter)
+% #3: parameter name
+\cs_new_protected:Npn \xsim_declare_parameter:nnn #1#2#3
+ {
+ \xsim_verbose:n { Declaring~ parameter~ `#3'. }
+ \xsim_if_parameter_exist:nF {#3}
+ {
+ \seq_put_right:Nn \l__xsim_parameters_seq {#3}
+ \bool_if:cT {c_#1_bool}
+ { \seq_put_right:Nn \l__xsim_fixed_parameters_seq {#3} }
+ \bool_if:cT {c_#2_bool}
+ { \seq_put_right:Nn \l__xsim_mandatory_parameters_seq {#3} }
+ }
+ }
+
+\cs_new_protected:Npn \xsim_remove_parameter:n #1
+ {
+ \xsim_verbose:n { Removing~ parameter~ `#1'. }
+ \xsim_if_parameter_exist:nT {#1}
+ { \seq_remove_all:Nn \l__xsim_parameters_seq {#1} }
+ }
+
+\prg_new_conditional:Npnn \xsim_if_parameter_exist:n #1 {p,T,F,TF}
+ {
+ \seq_if_in:NnTF \l__xsim_parameters_seq {#1}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\cs_generate_variant:Nn \xsim_if_parameter_exist:nT {x}
+
+\prg_new_conditional:Npnn \xsim_if_parameter_fixed:n #1 {p,T,F,TF}
+ {
+ \seq_if_in:NnTF \l__xsim_fixed_parameters_seq {#1}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+% #1: type
+% #2: csv list of parameters
+\cs_new_protected:Npn \xsim_set_parameters:nn #1#2
+ {
+ \clist_map_inline:nn {#2}
+ {
+ \seq_set_split:Nnx \l__xsim_tmpa_seq {=} { \tl_trim_spaces:n {##1} }
+ \xsim_set_parameter:nxx {#1}
+ { \seq_item:Nn \l__xsim_tmpa_seq {1} }
+ { \seq_item:Nn \l__xsim_tmpa_seq {2} }
+ }
+ }
+\cs_generate_variant:Nn \xsim_set_parameters:nn {nx}
+
+% #1: type
+\cs_new_protected:Npn \xsim_check_mandatory_parameters:n #1
+ {
+ \seq_map_inline:Nn \l__xsim_mandatory_parameters_seq
+ {
+ \xsim_if_parameter_set:nnF {#1} {##1}
+ { \msg_error:nnnn {xsim} {mandatory-parameter} {##1} {#1} }
+ }
+ }
+
+% #1: type
+\prg_new_conditional:Npnn \xsim_if_type:n #1 {p,T,F,TF}
+ {
+ \prop_if_exist:cTF {l__xsim_#1_type_parameters_prop}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+% #1: type
+% #2: parameter
+% #3: value
+\cs_new_protected:Npn \xsim_set_parameter:nnn #1#2#3
+ {
+ \xsim_if_parameter_exist:nT {#2}
+ {
+ \xsim_if_parameter_fixed:nTF {#1}
+ {
+ \xsim_if_parameter_set:nnTF {#1} {#2}
+ {
+ \msg_warning:nnnn {xsim} {parameter-fixed} {#1} {#2}
+ \xsim_verbose:n
+ {
+ Not~ setting~ already~ set~ and~ fixed~ parameter~ `#2'~
+ for~ type~ `#1'.
+ }
+ }
+ {
+ \xsim_verbose:n
+ { Setting~ parameter~ `#2'~ for~ type~ `#1'~ to~ `#3'. }
+ \prop_put:cnn {l__xsim_#1_type_parameters_prop} {#2} {#3}
+ }
+ }
+ {
+ \xsim_verbose:n
+ { Setting~ parameter~ `#2'~ for~ type~ `#1'~ to~ `#3'. }
+ \prop_put:cnn {l__xsim_#1_type_parameters_prop} {#2} {#3}
+ }
+ }
+ }
+\cs_generate_variant:Nn \xsim_set_parameter:nnn {nnx,nxx}
+
+% #1: type
+% #2: parameter
+\cs_new:Npn \xsim_get_parameter:nn #1#2
+ { \prop_item:cn {l__xsim_#1_type_parameters_prop} {#2} }
+\cs_generate_variant:Nn \xsim_get_parameter:nn {o}
+
+\prg_new_conditional:Npnn \xsim_if_parameter_set:nn #1#2 {p,T,F,TF}
+ {
+ \prop_if_in:cnTF {l__xsim_#1_type_parameters_prop} {#2}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+\cs_new:Npn \__xsim_get_parameter_tmp:n #1 {}
+\cs_generate_variant:Nn \__xsim_get_parameter_tmp:n {V}
+
+% #1: type
+% #2: parameter
+\cs_new_protected:Npn \xsim_get_parameter_if_set:nnTF #1#2#3#4
+ {
+ \group_begin:
+ \xsim_if_parameter_exist:nTF {#2}
+ {
+ \xsim_if_parameter_set:nnTF {#1} {#2}
+ {
+ \tl_set:Nx \ParameterValue
+ { \xsim_get_parameter:nn {#1} {#2} }
+ \cs_set:Npn \__xsim_get_parameter_tmp:n ##1 {#3}
+ \__xsim_get_parameter_tmp:V \ParameterValue
+ }
+ {#4}
+ }
+ { \msg_error:nnn {xsim} {unknown-parameter} {#2} }
+ \group_end:
+ }
+\cs_new_protected:Npn \xsim_get_parameter_if_set:nnT #1#2#3
+ { \xsim_get_parameter_if_set:nnTF {#1} {#2} {#3} {} }
+\cs_new_protected:Npn \xsim_get_parameter_if_set:nnF #1#2#3
+ { \xsim_get_parameter_if_set:nnTF {#1} {#2} {} {#3} }
+\cs_generate_variant:Nn \xsim_get_parameter_if_set:nnTF {o}
+
+% ----------------------------------------------------------------------------
+% inside the argument `#1' refers to the type
+\cs_new_protected:Npn \xsim_foreach_exercise_type:n #1
+ { \xsim_foreach_new_list_entry:nn {types} {#1} }
+
+\cs_new_protected:Npn \xsim_foreach_exercise_id:n #1
+ { \int_step_inline:nnnn {1} {1} { \g_xsim_max_id_int } {#1} }
+
+\cs_new:Npn \__xsim_loop_item:nnnnnn #1#2#3#4#5#6 {}
+\cs_generate_variant:Nn \__xsim_loop_item:nnnnnn {VVxxxx}
+
+% #1: type
+% #2: id
+% #3: boolean property
+\cs_new:Npn \__xsim_deliver_exercise_details:nnn #1#2#3
+ {
+ \xsim_if_in_list:nnT {#3} {#1-#2=={true}}
+ {
+ \tl_set_rescan:Nnx \l__xsim_tmpa_tl {} {#1}
+ \tl_set_rescan:Nnx \l__xsim_tmpb_tl {} {#2}
+ \__xsim_loop_item:VVxxxx
+ \l__xsim_tmpa_tl % #1
+ \l__xsim_tmpb_tl % #2
+ { \xsim_get_property:nnn {#1} {#2} {counter} } % #3
+ { \xsim_get_property:nnn {#1} {#2} {subtitle} } % #4
+ { \xsim_get_property:nnn {#1} {#2} {points} } % #5
+ { \xsim_get_property:nnn {#1} {#2} {bonus-points} } % #6
+ }
+ }
+
+% #1: boolean property
+% #2: loop code
+\xsim_define_loop_macro:Nnn \xsim_foreach_exercise_type_id:nn
+ {nnnnnn}
+ {
+ \xsim_foreach_exercise_type:n
+ {
+ \xsim_foreach_exercise_id:n
+ { \__xsim_deliver_exercise_details:nnn {##1} {####1} {#1} }
+ }
+ }
+
+\xsim_define_loop_macro:Nnn \xsim_foreach_exercise_id_type:nn
+ {nnnnnn}
+ {
+ \xsim_foreach_exercise_id:n
+ {
+ \xsim_foreach_exercise_type:n
+ { \__xsim_deliver_exercise_details:nnn {####1} {##1} {#1} }
+ }
+ }
+
+\cs_new_protected:Npn \xsim_define_exercise_type_variables:n #1
+ {
+ \prop_new:c {l__xsim_#1_type_parameters_prop}
+ \bool_new:c {l__xsim_#1_solution_print_bool}
+ \bool_new:c {l__xsim_#1_exercise_print_bool}
+ \bool_new:c {l__xsim_#1_exercise_use_bool}
+ }
+
+\cs_new_protected:Npn \xsim_define_counters:n #1
+ {
+ \xsim_verbose:n { Defining~ counters~ for~ new~ exercise~ type~ `#1' }
+ \cs_if_exist:cF { c@ \xsim_get_parameter:nn {#1} {counter} }
+ { \newcounter { \xsim_get_parameter:nn {#1} {counter} } }
+ \cs_if_exist:cF { c@ \xsim_get_parameter:nn {#1} {solution-counter} }
+ { \newcounter { \xsim_get_parameter:nn {#1} {solution-counter} } }
+ \cs_if_exist:cF { c@ number of \xsim_get_parameter:nn {#1} {exercise-env} s }
+ { \newcounter { number of \xsim_get_parameter:nn {#1} {exercise-env} s } }
+ }
+
+% #1: type
+% #2: id
+\cs_new_protected:Npn \xsim_step_exercise_counter:nn #1#2
+ {
+ \refstepcounter { \xsim_get_parameter:nn {#1} {counter} }
+ \xsim_set_properties:nnx {#1} {#2}
+ {
+ counter = \use:c { the \xsim_get_parameter:nn {#1} {counter} } ,
+ counter-value = \arabic { \xsim_get_parameter:nn {#1} {counter} }
+ }
+ }
+\cs_generate_variant:Nn \xsim_step_exercise_counter:nn {nV}
+
+% #1: type
+\cs_new_protected:Npn \xsim_declare_exercise_type:nn #1#2
+ {
+ \xsim_if_type:nTF {#1}
+ { \msg_error:nnn {xsim} {type-exists} {#1} }
+ {
+ \xsim_verbose:n { Declaring~ new~ exercise~ type~ `#1'. }
+ \xsim_define_exercise_type_variables:n {#1}
+ \xsim_add_to_list:nn {types} {#1}
+ \xsim_set_parameters:nn {#1} {#2}
+ \xsim_set_parameter:nnx {#1} {number}
+ { number of \xsim_get_parameter:nn {#1} {exercise-env} s }
+ \xsim_check_mandatory_parameters:n {#1}
+ \xsim_if_parameter_set:nnF {#1} {counter}
+ {
+ \xsim_set_parameters:nx {#1}
+ { counter = \xsim_get_parameter:nn {#1} {exercise-env} }
+ }
+ \xsim_if_parameter_set:nnF {#1} {solution-counter}
+ {
+ \xsim_set_parameters:nx {#1}
+ { solution-counter = \xsim_get_parameter:nn {#1} {solution-env} }
+ }
+ \xsim_define_counters:n {#1}
+ \xsim_verbose:n { Defining~ options~ for~ new~ exercise~ type~ `#1' }
+ \keys_define:nx {xsim}
+ {
+ \xsim_get_parameter:nn {#1} {solution-env} / print
+ .bool_set:N = \exp_not:c {l__xsim_#1_solution_print_bool} ,
+ \xsim_get_parameter:nn {#1} {solution-env} / print
+ .initial:n = false ,
+ \xsim_get_parameter:nn {#1} {exercise-env} / print
+ .bool_set:N = \exp_not:c {l__xsim_#1_exercise_print_bool} ,
+ \xsim_get_parameter:nn {#1} {exercise-env} / print
+ .initial:n = true ,
+ \xsim_get_parameter:nn {#1} {exercise-env} / use
+ .bool_set:N = \exp_not:c {l__xsim_#1_exercise_use_bool} ,
+ \xsim_get_parameter:nn {#1} {exercise-env} / use
+ .initial:n = true ,
+ \xsim_get_parameter:nn {#1} {exercise-env} / within .code:n =
+ \exp_not:N \@addtoreset { \xsim_get_parameter:nn {#1} {counter} } {####1},
+ \xsim_get_parameter:nn {#1} {exercise-env} / the-counter .code:n =
+ \cs_set:cpn {the \xsim_get_parameter:nn {#1} {counter} } {####1} ,
+ \xsim_get_parameter:nn {#1} {exercise-env} / template
+ .code:n = \xsim_set_parameter:nnn {#1} {exercise-template} {####1} ,
+ \xsim_get_parameter:nn {#1} {solution-env} / template
+ .code:n = \xsim_set_parameter:nnn {#1} {solution-template} {####1} ,
+ \xsim_get_parameter:nn {#1} {exercise-env} / name
+ .code:n = \xsim_set_parameter:nnn {#1} {exercise-name} {####1} ,
+ \xsim_get_parameter:nn {#1} {solution-env} / name
+ .code:n = \xsim_set_parameter:nnn {#1} {solution-name} {####1}
+ }
+ \xsim_new_environment:nn {#1} {exercise}
+ \xsim_new_environment:nn {#1} {solution}
+ }
+ }
+
+% #1: type
+% #2: id
+\prg_new_conditional:Npnn \xsim_if_exercise_exists:nn #1#2 {T,F,TF}
+ {
+ \__xsim_set_file_signature:nnn {#1} {#2} {exercise}
+ \file_if_exist:VTF \l__xsim_file_path_and_name_tl
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\cs_generate_variant:Nn \xsim_if_exercise_exists:nnT {VV}
+
+% #1: type
+% #2: options
+\cs_new_protected:Npn \xsim_start_exercise:nn #1#2
+ {
+ \xsim_if_insert_mode:F
+ {
+ \int_gincr:N \g_xsim_id_int
+ \int_compare:nF { \g_xsim_max_id_int > \g_xsim_id_int }
+ { \int_gset_eq:NN \g_xsim_max_id_int \g_xsim_id_int }
+ \xsim_set_properties:nxx {#1} { \int_use:N \g_xsim_id_int }
+ {
+ print = true ,
+ use = true ,
+ \exp_not:n {#2} ,
+ id = \int_use:N \g_xsim_id_int ,
+ section-value = \arabic{section} ,
+ section = \thesection ,
+ page-value = \arabic{page} ,
+ sectioning =
+ { \cs_if_exist:NTF \chapter { \arabic{chapter} } {0} }
+ { \arabic {section} }
+ { \arabic {subsection} }
+ { \arabic {subsubsection} }
+ { \arabic {paragraph} } ,
+ page = \thepage ,
+ used = false
+ }
+ \xsim_if_property_exist:nT {chapter}
+ {
+ \xsim_set_properties:nxx {#1} { \int_use:N \g_xsim_id_int }
+ {
+ chapter-value = \arabic{chapter} ,
+ chapter = \thechapter
+ }
+ }
+ \tl_gclear:N \g_xsim_exercise_id_tl
+ \xsim_gsave_property:nxnN {#1} { \int_use:N \g_xsim_id_int } {id}
+ \g_xsim_exercise_id_tl
+ \tl_gset_eq:NN \ExerciseID \g_xsim_exercise_id_tl
+ \tl_gset:Nn \ExerciseType {#1}
+ \xsim_verbose:x
+ {
+ Starting~ exercise~ type~ `#1' with~ id~
+ ` \g_xsim_exercise_id_tl '.
+ }
+ \xsim_exercise_if_use:nVTF {#1} \g_xsim_exercise_id_tl
+ {
+ \stepcounter { \xsim_get_parameter:nn {#1} {number} }
+ % don't step if in active collection:
+ \xsim_if_collections_active:TF
+ {
+ \xsim_set_property:nVnn {#1} \g_xsim_exercise_id_tl
+ {print}
+ {false}
+ }
+ {
+ \xsim_step_exercise_counter:nV {#1} \g_xsim_exercise_id_tl
+ \xsim_set_property:nVnn {#1} \g_xsim_exercise_id_tl
+ {used}
+ {true}
+ }
+ \xsim_set_property:nVnn {#1} \g_xsim_exercise_id_tl
+ {use}
+ {true}
+ \xsim_update_goals:nV {#1} \g_xsim_exercise_id_tl
+ }
+ {
+ \xsim_set_properties:nVn {#1} \g_xsim_exercise_id_tl
+ { use = false , print = false }
+ }
+ \xsim_update_collections:nV {#1} \g_xsim_exercise_id_tl
+ }
+ }
+
+\cs_new_protected:Npn \xsim_stop_exercise:n #1 {}
+
+% ----------------------------------------------------------------------------
+% #1: type
+% #2: id
+\prg_new_protected_conditional:Npnn \xsim_exercise_if_print:nn #1#2 {T,F,TF}
+ {
+ \xsim_if_boolean_property:nnnTF {#1} {#2} {print!}
+ { \prg_return_true: }
+ {
+ \bool_set_true:N \l____xsim_print_bool
+ \bool_if:cF {l__xsim_#1_exercise_print_bool}
+ { \bool_set_false:N \l____xsim_print_bool }
+ \xsim_if_tagged:nnF {#1} {#2}
+ { \bool_set_false:N \l____xsim_print_bool }
+ \xsim_if_boolean_property:nnnF {#1} {#2} {print}
+ { \bool_set_false:N \l____xsim_print_bool }
+ \bool_if:NTF \l____xsim_print_bool
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+ }
+\cs_generate_variant:Nn \xsim_exercise_if_print:nnT {nV}
+\cs_generate_variant:Nn \xsim_exercise_if_print:nnTF {nV}
+
+% #1: type
+% #2: id
+\prg_new_protected_conditional:Npnn \xsim_exercise_if_use:nn #1#2 {T,F,TF}
+ {
+ \xsim_if_boolean_property:nnnTF {#1} {#2} {use!}
+ { \prg_return_true: }
+ {
+ \xsim_exercise_if_print:nnTF {#1} {#2}
+ { \prg_return_true: }
+ {
+ \bool_if:cTF {l__xsim_#1_exercise_use_bool}
+ {
+ \xsim_if_boolean_property:nnnTF {#1} {#2} {use}
+ {
+ \xsim_if_tagged:nnTF {#1} {#2}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+ { \prg_return_false: }
+ }
+ { \prg_return_false: }
+ }
+
+ }
+ }
+\cs_generate_variant:Nn \xsim_exercise_if_use:nnT {nV}
+\cs_generate_variant:Nn \xsim_exercise_if_use:nnTF {nV}
+
+% #1: type
+% #2: id
+% #3: exercise|solution
+\prg_new_protected_conditional:Npnn \xsim_if_print:nnn #1#2#3 {T,F,TF}
+ {
+ \use:c {xsim_#3_if_print:nnTF} {#1} {#2}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+% ----------------------------------------------------------------------------
+
+\xsim_new_aux_cs:cpn {total-number} #1
+ { \int_gset:Nn \g_xsim_max_id_int {#1} }
+
+\RequirePackage {etoolbox}
+
+\AtEndPreamble
+ {
+ \xsim_foreach_exercise_type:n
+ {
+ \tl_new:c { number of #1 s }
+ \tl_set:cn { number of #1 s } {0}
+ \xsim_new_aux_cs:cpn {#1} ##1
+ { \tl_gset:cn { number of #1 s } {##1} }
+ }
+ }
+
+\AtBeginDocument
+ {
+ \xsim_foreach_exercise_type:n
+ {
+ \xsim_write_to_aux:x
+ { \token_to_str:N \providecommand \token_to_str:N \numberof #1 s {} }
+ }
+ }
+
+\AtEndDocument
+ {
+ \xsim_if_clear_aux:T
+ {
+ \int_compare:nNnF { \g_xsim_max_id_int } = { \g_xsim_id_int }
+ { \xsim_empty_lists: }
+ }
+ \xsim_add_cs_to_aux:nn {total-number} { { \int_use:N \g_xsim_id_int } }
+ \xsim_foreach_exercise_type:n
+ {
+ \xsim_add_cs_to_aux:nn {#1}
+ { { \arabic { \xsim_get_parameter:nn {#1} {number} } } }
+ }
+ \xsim_update_list:n {types}
+ }
+
+% ----------------------------------------------------------------------------
+\file_input_stop:
diff --git a/macros/latex/contrib/xsim/code/xsim.goals.code.tex b/macros/latex/contrib/xsim/code/xsim.goals.code.tex
new file mode 100644
index 0000000000..ab8662ad71
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsim.goals.code.tex
@@ -0,0 +1,307 @@
+% ----------------------------------------------------------------------------
+% the XSIM package - goals module
+%
+% eXercise Sheets IMproved
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+\XSIMmodule{goals}{counting of points and bonus points}
+
+\xsim_load_modules:n {properties,exercises}
+
+\msg_new:nnn {xsim} {goal-unknown}
+ { The~ goal~ `#1'~ has~ never~ been~ declared~ \msg_line_context: }
+
+\seq_new:N \l__xsim_goals_seq
+\tl_new:N \l_xism_grading_table_template_tl
+\tl_new:N \l_xism_grading_table_exercise_type_tl
+
+% #1: name
+\cs_new_protected:Npn \xsim_declare_exercise_goal:n #1
+ {
+ \xsim_if_goal_exists:nF {#1}
+ {
+ \xsim_verbose:n { Declaring~ new~ goal~ `#1'. }
+ \xsim_declare_property:nnnn
+ { \c_false_bool }
+ { \c_false_bool }
+ { \c_false_bool }
+ {#1}
+ \seq_put_right:Nn \l__xsim_goals_seq {#1}
+ \fp_gzero_new:c {g__xsim_goal_#1_fp}
+ \fp_gzero_new:c {l__xsim_current_goal_#1_fp}
+ \fp_if_exist:cF {g__xsim_total_goal_#1_fp}
+ { \fp_new:c {g__xsim_total_goal_#1_fp} }
+ \fp_if_exist:cF {g__xsim_total_goal_recorded_#1_fp}
+ { \fp_new:c {g__xsim_total_goal_recorded_#1_fp} }
+ \AtBeginDocument
+ {
+ \xsim_foreach_exercise_type:n
+ {
+ \fp_gzero_new:c {g__xsim_##1_goal_#1_fp}
+ \fp_if_exist:cF {g__xsim_total_##1_goal_#1_fp}
+ { \fp_new:c {g__xsim_total_##1_goal_#1_fp} }
+ }
+ }
+ }
+ }
+
+\prg_new_conditional:Npnn \xsim_if_goal_exists:n #1 {p,T,F,TF}
+ {
+ \seq_if_in:NnTF \l__xsim_goals_seq {#1}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+% #1: exercise type
+% #2: id
+% #3: goal
+\cs_new_protected:Npn \xsim_update_goal:nnn #1#2#3
+ {
+ \xsim_if_goal_exists:nT {#3}
+ {
+ \fp_gadd:cn {g__xsim_goal_#3_fp}
+ { 0 \xsim_get_property:nnn {#1} {#2} {#3} }
+ \fp_gadd:cn {g__xsim_#1_goal_#3_fp}
+ { 0 \xsim_get_property:nnn {#1} {#2} {#3} }
+ }
+ }
+
+% #1: exercise type
+% #2: id
+\cs_new_protected:Npn \xsim_update_goals:nn #1#2
+ {
+ \xsim_foreach_goal:n
+ { \xsim_update_goal:nnn {#1} {#2} {##1} }
+ }
+\cs_generate_variant:Nn \xsim_update_goals:nn {nV}
+
+\cs_new_protected:Npn \xsim_foreach_goal:n #1
+ { \seq_map_inline:Nn \l__xsim_goals_seq {#1} }
+
+% #1: exercise type
+% #2: id
+% #3: goal
+% #4: value
+\cs_new_protected:Npn \xsim_addto_goal:nnnn #1#2#3#4
+ {
+ \xsim_if_goal_exists:nT {#3}
+ {
+ \xsim_verbose:n { Adding~ `#4'~ to~ `#3'~ goal~ of~ #1~ #2 }
+ \fp_gadd:cn {g__xsim_goal_#3_fp} {#4}
+ \fp_gadd:cn {g__xsim_#1_goal_#3_fp} {#4}
+ \fp_add:cn {l__xsim_current_goal_#3_fp} {#4}
+ }
+ }
+\cs_generate_variant:Nn \xsim_addto_goal:nnnn {xx,nVnx}
+
+% #1: exercise type
+% #2: id
+% #3: goal
+% #4: relation and value
+\prg_new_conditional:Npnn \xsim_if_goal_value:nnnn #1#2#3#4 {p,T,F,TF}
+ {
+ \fp_compare:nTF
+ { (0 \xsim_get_property:nnn {#1} {#2} {#3}) #4 }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\cs_generate_variant:Nn \xsim_if_goal_value:nnnnTF {xx}
+
+% ----------------------------------------------------------------------------
+% a function which can be used to modify how the numbers of a goal are
+% printed:
+\cs_new_protected:Npn \__xsim_print_goal:n #1 {#1}
+\cs_generate_variant:Nn \__xsim_print_goal:n {x}
+
+% #1: value
+% #2: value == 1
+% #3: value != 1
+\cs_new_protected:Npn \xsim_print_goal:nnn #1#2#3
+ {
+ \__xsim_print_goal:x { \fp_to_decimal:n {#1} }
+ \fp_compare:nTF { (#1) = 1 } {#2} {#3}
+ }
+
+% #1: exercise type
+% #2: goal
+% #3: sum == 1
+% #4: sum != 1
+\cs_new_protected:Npn \xsim_print_goal_sum:nnnn #1#2#3#4
+ { \xsim_print_goal:nnn { \use:c {g__xsim_total_#1_goal_#2_fp} } {#3} {#4} }
+
+% #1: goal
+% #2: sum == 1
+% #3: sum != 1
+\cs_new_protected:Npn \xsim_print_total_goal_sum:nnn #1#2#3
+ { \xsim_print_goal:nnn { \use:c {g__xsim_total_goal_#1_fp} } {#2} {#3} }
+
+% ----------------------------------------------------------------------------
+% the same as the last to functions but for a list of goals:
+
+% #1: fp variable to be set
+% #2: list of goals (separated with +)
+% #3: function to be applied to goal names
+\cs_new_protected:Npn \__xsim_exercise_goals_sum_aux:Nnn #1#2#3
+ {
+ \seq_set_split:Nnn \l__xsim_tmpa_seq {+} {#2}
+ \seq_set_map:NNn \l__xsim_tmpb_seq \l__xsim_tmpa_seq {#3}
+ \fp_set:Nn #1 { \seq_use:Nn \l__xsim_tmpb_seq {+} }
+ }
+
+% #1: fp variable
+% #2: exercise type
+% #3: list of goals (separated with +)
+\cs_new_protected:Npn \__xsim_exercise_goals_sum:Nnn #1#2#3
+ {
+ \__xsim_exercise_goals_sum_aux:Nnn #1 {#3}
+ { \exp_not:c {g__xsim_total_#2_goal_ \tl_trim_spaces:n {##1} _fp} }
+ }
+
+% #1: fp variable
+% #2: list of goals (separated with +)
+\cs_new_protected:Npn \__xsim_exercise_goals_sum:Nn #1#2
+ {
+ \__xsim_exercise_goals_sum_aux:Nnn #1 {#2}
+ { \exp_not:c {g__xsim_total_goal_ \tl_trim_spaces:n {##1} _fp} }
+ }
+
+% #1: exercise type
+% #2: list of goals (separated with +)
+% #3: sum == 1
+% #4: sum != 1
+\cs_new_protected:Npn \xsim_print_goals_sum:nnnn #1#2#3#4
+ {
+ \__xsim_exercise_goals_sum:Nnn \l__xsim_tmpa_fp {#1} {#2}
+ \xsim_print_goal:nnn { \l__xsim_tmpa_fp } {#3} {#4}
+ }
+
+% #1: list of goals (separated with +)
+% #2: sum == 1
+% #3: sum != 1
+\cs_new_protected:Npn \xsim_print_total_goals_sum:nnn #1#2#3
+ {
+ \__xsim_exercise_goals_sum:Nn \l__xsim_tmpa_fp {#1}
+ \xsim_print_goal:nnn { \l__xsim_tmpa_fp } {#2} {#3}
+ }
+
+% ----------------------------------------------------------------------------
+% #1: exercise type
+% #2: goal
+% #3: relation and value
+\prg_new_conditional:Npnn \xsim_if_goal_sum:nnn #1#2#3 {p,T,F,TF}
+ {
+ \fp_compare:nTF
+ { ( \use:c {g__xsim_total_#1_goal_#2_fp} ) #3 }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+% #1: exercise type
+% #2: list of goals (separated with +)
+% #3: relation and value
+\prg_new_protected_conditional:Npnn \xsim_if_goals_sum:nnn #1#2#3 {T,F,TF}
+ {
+ \__xsim_exercise_goals_sum:Nnn \l__xsim_tmpa_fp {#1} {#2}
+ \fp_compare:nTF
+ { ( \l__xsim_tmpa_fp ) #3 }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+% #1: goal
+% #2: relation and value
+\prg_new_conditional:Npnn \xsim_if_total_goal_sum:nn #1#2 {p,T,F,TF}
+ {
+ \fp_compare:nTF
+ { ( \use:c {g__xsim_total_goal_#1_fp} ) #2 }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+% #1: list of goals (separated with +)
+% #2: relation and value
+\prg_new_protected_conditional:Npnn \xsim_if_total_goals_sum:nn #1#2 {T,F,TF}
+ {
+ \__xsim_exercise_goals_sum:Nn \l__xsim_tmpa_fp {#1}
+ \fp_compare:nTF
+ { ( \l__xsim_tmpa_fp ) #2 }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+% ----------------------------------------------------------------------------
+% #1: exercise type
+% #2: goal
+% #3: value
+\xsim_new_aux_cs:cpn {goal} #1#2#3
+ { \fp_gset:cn {g__xsim_total_#1_goal_#2_fp} {#3} }
+
+% #1: goal
+% #2: value
+\xsim_new_aux_cs:cpn {totalgoal} #1#2
+ { \fp_gset:cn {g__xsim_total_goal_#1_fp} {#2} }
+
+\AtBeginDocument
+ {
+ \seq_map_inline:Nn \l__xsim_goals_seq
+ {
+ \fp_gset_eq:cc
+ {g__xsim_total_goal_recorded_#1_fp}
+ {g__xsim_total_goal_#1_fp}
+ }
+ }
+
+\AtEndDocument
+ {
+ \seq_map_inline:Nn \l__xsim_goals_seq
+ {
+ \xsim_foreach_exercise_type:n
+ {
+ \xsim_add_cs_to_aux:nn {goal}
+ { {##1} {#1} { \fp_to_decimal:c {g__xsim_##1_goal_#1_fp} } }
+ }
+ \tl_set:Nx \l__xsim_tmpa_tl
+ { \fp_to_decimal:c {g__xsim_total_goal_#1_fp} }
+ \tl_set:Nx \l__xsim_tmpb_tl
+ { \fp_to_decimal:c {g__xsim_total_goal_recorded_#1_fp} }
+ \tl_if_eq:NNF \l__xsim_tmpa_tl \l__xsim_tmpb_tl { \xsim_rerun: }
+ \xsim_add_cs_to_aux:nn {totalgoal}
+ { {#1} { \fp_to_decimal:c {g__xsim_goal_#1_fp} } }
+ }
+ }
+
+% ----------------------------------------------------------------------------
+
+\keys_define:nn {xsim}
+ {
+ goal-print .code:n =
+ \cs_set_protected:Npn \__xsim_print_goal:n ##1 {#1} ,
+ grading-table/template .tl_set:N = \l_xism_grading_table_template_tl ,
+ grading-table/template .initial:n = default ,
+ grading-table/type .tl_set:N = \l_xism_grading_table_exercise_type_tl ,
+ grading-table/type .initial:n =
+ }
+
+% ----------------------------------------------------------------------------
+\file_input_stop:
diff --git a/macros/latex/contrib/xsim/code/xsim.grades.code.tex b/macros/latex/contrib/xsim/code/xsim.grades.code.tex
new file mode 100644
index 0000000000..4c25e99b45
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsim.grades.code.tex
@@ -0,0 +1,153 @@
+% ----------------------------------------------------------------------------
+% the XSIM package - grades module
+%
+% eXercise Sheets IMproved
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+\XSIMmodule{grades}{distribute goal sums to grades}
+
+\xsim_load_modules:n {goals}
+
+\msg_new:nnn {xsim} {grade-unknown}
+ { The~ grade~ `#1'~ has~ never~ been~ declared~ \msg_line_context: }
+
+\bool_new:N \l__xsim_grades_half_bool
+\fp_new:N \l__xsim_grade_round_fp
+\fp_zero:N \l__xsim_grade_round_fp
+\prop_new:N \l__xsim_relative_grades_prop
+\tl_new:N \l__xsim_grade_split_tl
+
+\cs_new:Npn \__xsim_fp_round_to_half:n #1
+ { round( 2*(#1),0)/2 }
+
+\cs_new:Npn \__xsim_grades_round:n #1
+ { round ( #1 , \l__xsim_grade_round_fp ) }
+
+% #1: factor
+% #2: goal sum
+\cs_new:Npn \__xsim_grade_rounded:nn #1#2
+ {
+ \bool_if:NTF \l__xsim_grades_half_bool
+ { \__xsim_fp_round_to_half:n }
+ { \__xsim_grades_round:n }
+ { (#1) * (#2) }
+ }
+
+\cs_new_protected:Npn \xsim_declare_relative_grades:n #1
+ {
+ \seq_set_split:NVn \l__xsim_tmpa_seq \l__xsim_grade_split_tl {#1}
+ \seq_map_inline:Nn \l__xsim_tmpa_seq
+ { \__xsim_declare_relative_grade:w ##1 \q_stop }
+ \xsim_verbose:n {Declaring~ relative~ grade~ distribution}
+ }
+
+\cs_new_protected:Npn \__xsim_declare_relative_grade:w #1 = #2 \q_stop
+ {
+ \tl_set:Nx \l__xsim_tmpa_tl { \tl_trim_spaces:n {#1} }
+ \tl_set:Nx \l__xsim_tmpb_tl { \tl_trim_spaces:n {#2} }
+ \prop_put:NVV \l__xsim_relative_grades_prop
+ \l__xsim_tmpa_tl
+ \l__xsim_tmpb_tl
+ }
+
+% #1: grade
+% #2: sum of points
+% #3: code after grade requirement if == 1
+% #4: code after grade requirement if != 1
+\cs_new_protected:Npn \xsim_get_grade:nnnn #1#2#3#4
+ {
+ \prop_get:NnNTF \l__xsim_relative_grades_prop {#1} \l__xsim_tmpa_tl
+ {
+ \xsim_print_goal:nnn
+ {
+ min(
+ \__xsim_grade_rounded:nn
+ { \l__xsim_tmpa_tl }
+ {#2} ,
+ #2
+ )
+ }
+ {#3}
+ {#4}
+ }
+ { \msg_error:nnn {xsim} {grade-unknown} {#1} }
+ }
+
+% #1: grade
+% #2: goal
+% #3: code after grade requirement if == 1
+% #4: code after grade requirement if != 1
+\cs_new_protected:Npn \xsim_get_grade_goal:nnnn #1#2#3#4
+ {
+ \xsim_if_goal_exists:nTF {#2}
+ {
+ \xsim_get_grade:nnnn
+ {#1}
+ { \use:c {g__xsim_total_goal_#2_fp} }
+ {#3}
+ {#4}
+ }
+ { \msg_error:nnn {xsim} {goal-unknown} {#2} }
+ }
+
+% #1: grade
+% #2: goal list (separated with +)
+% #3: code after grade requirement if == 1
+% #4: code after grade requirement if != 1
+\cs_new_protected:Npn \xsim_get_grade_requirement:nnnn #1#2#3#4
+ {
+ \seq_set_split:Nnn \l__xsim_tmpa_seq {+} {#2}
+ \fp_zero:N \l__xsim_tmpa_fp
+ \seq_map_inline:Nn \l__xsim_tmpa_seq
+ {
+ \fp_add:Nn \l__xsim_tmpa_fp
+ { \use:c {g__xsim_total_goal_\tl_trim_spaces:n {##1} _fp} }
+ }
+ \xsim_get_grade:nnnn {#1} { \l__xsim_tmpa_fp } {#3} {#4}
+ }
+
+% #1: grade
+% #2: code after grade requirement if == 1
+% #3: code after grade requirement if != 1
+\cs_new_protected:Npn \xsim_get_absolute_grade_requirement:nnn #1#2#3
+ {
+ \fp_zero:N \l__xsim_tmpa_fp
+ \xsim_foreach_goal:n
+ {
+ \fp_add:Nn \l__xsim_tmpa_fp
+ { \use:c {g__xsim_total_goal_##1_fp} }
+ }
+ \xsim_get_grade:nnnn {#1} { \l__xsim_tmpa_fp } {#2} {#3}
+ }
+
+\keys_define:nn {xsim/grades}
+ {
+ round .fp_set:N = \l__xsim_grade_round_fp ,
+ half .bool_set:N = \l__xsim_grades_half_bool ,
+ split .tl_set:N = \l__xsim_grade_split_tl ,
+ split .initial:n = {,}
+ }
+
+% ----------------------------------------------------------------------------
+\file_input_stop:
diff --git a/macros/latex/contrib/xsim/code/xsim.interface.code.tex b/macros/latex/contrib/xsim/code/xsim.interface.code.tex
new file mode 100644
index 0000000000..32beb12208
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsim.interface.code.tex
@@ -0,0 +1,649 @@
+% ----------------------------------------------------------------------------
+% the XSIM package - interface module
+%
+% eXercise Sheets IMproved
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+\XSIMmodule{interface}{user interface}
+
+\NewDocumentCommand \DeclareExerciseEnvironmentTemplate {m+m+m}
+ { \xsim_declare_environment_template:nnn {#1} {#2} {#3} }
+\@onlypreamble \DeclareExerciseEnvironmentTemplate
+
+\NewDocumentCommand \DeclareExerciseHeadingTemplate {m+m}
+ { \xsim_declare_heading_template:nn {#1} {#2} }
+\@onlypreamble \DeclareExerciseHeadingTemplate
+
+\NewDocumentCommand \DeclareExerciseTableTemplate {m+m}
+ { \xsim_declare_table_template:nn {#1} {#2} }
+\@onlypreamble \DeclareExerciseTableTemplate
+
+\NewDocumentCommand \UseExerciseTemplate {mm}
+ { \xsim_use_template:nn {#1} {#2} }
+
+% ----------------------------------------------------------------------------
+
+\NewDocumentCommand \DeclareExerciseType {mm}
+ { \xsim_declare_exercise_type:nn {#1} {#2} }
+\@onlypreamble \DeclareExerciseType
+
+\NewExpandableDocumentCommand \numberofusedexercises {}
+ { \int_use:N \g_xsim_max_id_int }
+
+% ----------------------------------------------------------------------------
+
+\ProvideDocumentCommand \IfExistSolutionTF {+m+m}
+ { \xsim_if_solution_exists:ooTF {\ExerciseType} {\ExerciseID} {#1} {#2} }
+
+\NewDocumentCommand \IfExistSolutionT {+m}
+ { \IfExistSolutionTF {#1} {} }
+
+\NewDocumentCommand \IfExistSolutionF {+m}
+ { \IfExistSolutionTF {} {#1} }
+
+\NewExpandableDocumentCommand \IfInsideSolutionTF {+m+m}
+ { \xsim_if_inside_solution:TF {#1} {#2} }
+
+\NewExpandableDocumentCommand \IfInsideSolutionT {+m}
+ { \IfInsideSolutionTF {#1} {} }
+
+\NewExpandableDocumentCommand \IfInsideSolutionF {+m}
+ { \IfInsideSolutionTF {} {#1} }
+
+\NewExpandableDocumentCommand \IfSolutionPrintTF {+m+m}
+ { \xsim_if_solution_print:nTF {\ExerciseType} {#1} {#2} }
+
+\NewExpandableDocumentCommand \IfSolutionPrintT {+m}
+ { \IfSolutionPrintTF {#1} {} }
+
+\NewExpandableDocumentCommand \IfSolutionPrintF {+m}
+ { \IfSolutionPrintTF {} {#1} }
+
+% ----------------------------------------------------------------------------
+
+\NewDocumentCommand \DeclareExerciseProperty {t!st-m}
+ {
+ \IfBooleanTF {#2}
+ {
+ \IfBooleanTF {#3}
+ {
+ \xsim_declare_property:nnnn
+ { \c_false_bool }
+ { \c_true_bool }
+ { \c_true_bool }
+ {#4}
+ }
+ {
+ \xsim_declare_property:nnnn
+ { \c_false_bool }
+ { \c_true_bool }
+ { \c_false_bool }
+ {#4}
+ }
+ }
+ {
+ \IfBooleanTF {#1}
+ {
+ \IfBooleanTF {#3}
+ {
+ \xsim_declare_property:nnnn
+ { \c_true_bool }
+ { \c_false_bool }
+ { \c_true_bool }
+ {#4}
+ }
+ {
+ \xsim_declare_property:nnnn
+ { \c_true_bool }
+ { \c_false_bool }
+ { \c_false_bool }
+ {#4}
+ }
+ }
+ {
+ \IfBooleanTF {#3}
+ {
+ \xsim_declare_property:nnnn
+ { \c_false_bool }
+ { \c_false_bool }
+ { \c_true_bool }
+ {#4}
+ }
+ {
+ \xsim_declare_property:nnnn
+ { \c_false_bool }
+ { \c_false_bool }
+ { \c_false_bool }
+ {#4}
+ }
+ }
+ }
+ }
+\@onlypreamble \DeclareExerciseProperty
+
+\NewDocumentCommand \DeclareExercisePropertyAlias {mm}
+ { \xsim_make_property_alias:nn {#1} {#2} }
+\@onlypreamble \DeclareExercisePropertyAlias
+
+\NewDocumentCommand \SetExerciseProperty {m+m}
+ { \xsim_set_property:xxnn {\ExerciseType} {\ExerciseID} {#1} {#2} }
+
+\NewDocumentCommand \SetExpandedExerciseProperty {m+m}
+ { \xsim_set_property:xxnx {\ExerciseType} {\ExerciseID} {#1} {#2} }
+
+\NewDocumentCommand \ExerciseSetProperty {mmm+m}
+ { \xsim_set_property:nnnn {#1} {#2} {#3} {#4} }
+
+\NewDocumentCommand \ExerciseSetExpandedProperty {mmm+m}
+ { \xsim_set_property:nnnx {#1} {#2} {#3} {#4} }
+
+\NewExpandableDocumentCommand \IfExercisePropertyExistTF {+m+m+m}
+ { \xsim_if_property_exist:nTF {#1} {#2} {#3} }
+
+\NewExpandableDocumentCommand \IfExercisePropertyExistT {+m+m}
+ { \IfExercisePropertyExistTF {#1} {#2} {} }
+
+\NewExpandableDocumentCommand \IfExercisePropertyExistF {+m+m}
+ { \IfExercisePropertyExistTF {#1} {} {#2} }
+
+\NewDocumentCommand \IfExercisePropertySetTF {m+m+m}
+ { \xsim_if_property_set:xxnTF {\ExerciseType} {\ExerciseID} {#1} {#2} {#3} }
+
+\NewDocumentCommand \IfExercisePropertySetT {m+m}
+ { \IfExercisePropertySetTF {#1} {#2} {} }
+
+\NewDocumentCommand \IfExercisePropertySetF {m+m}
+ { \IfExercisePropertySetTF {#1} {} {#2} }
+
+\NewExpandableDocumentCommand \GetExerciseProperty {m}
+ { \xsim_get_property:oon {\ExerciseType} {\ExerciseID} {#1} }
+
+\NewExpandableDocumentCommand \GetExerciseAliasProperty {m}
+ {
+ \xsim_get_property:oof
+ {\ExerciseType}
+ {\ExerciseID}
+ { \xsim_property_alias:n {#1} }
+ }
+
+\NewDocumentCommand \GetExercisePropertyTF {m+m+m}
+ {
+ \xsim_get_property_if_set:oonTF {\ExerciseType} {\ExerciseID} {#1}
+ {#2}
+ {#3}
+ }
+
+\NewDocumentCommand \GetExercisePropertyT {m+m}
+ { \GetExercisePropertyTF {#1} {#2} {} }
+\NewDocumentCommand \GetExercisePropertyF {m+m+m}
+ { \GetExercisePropertyTF {#1} {} {#2} }
+
+\NewExpandableDocumentCommand \GetExerciseIdForProperty {mm}
+ { \xsim_get_id_for_property:nn {#1} {#2} }
+
+\NewExpandableDocumentCommand \GetExerciseTypeForProperty {mm}
+ { \xsim_get_type_for_property:nn {#1} {#2} }
+
+\NewExpandableDocumentCommand \IfExerciseBooleanPropertyTF {+m+m+m}
+ {
+ \xsim_if_boolean_property:oonTF {\ExerciseType} {\ExerciseID} {#1}
+ {#2}
+ {#3}
+ }
+
+\NewExpandableDocumentCommand \IfExerciseBooleanPropertyT {+m+m}
+ { \IfExerciseBooleanPropertyTF {#1} {#2} {} }
+\NewExpandableDocumentCommand \IfExerciseBooleanPropertyF {+m+m}
+ { \IfExerciseBooleanPropertyTF {#1} {} {#2} }
+
+\NewDocumentCommand \SaveExerciseProperty {mm}
+ { \xsim_save_property:xxnN {\ExerciseType} {\ExerciseID} {#1} #2 }
+
+\NewDocumentCommand \GlobalSaveExerciseProperty {mm}
+ { \xsim_gsave_property:xxnN {\ExerciseType} {\ExerciseID} {#1} #2 }
+
+% ----------------------------------------------------------------------------
+
+\NewDocumentCommand \DeclareExerciseParameter {st!m}
+ {
+ \IfBooleanTF {#1}
+ {
+ \IfBooleanTF {#2}
+ { \xsim_declare_parameter:nnn {true} {true} {#3} }
+ { \xsim_declare_parameter:nnn {true} {false} {#3} }
+ }
+ {
+ \IfBooleanTF {#2}
+ { \xsim_declare_parameter:nnn {false} {true} {#3} }
+ { \xsim_declare_parameter:nnn {false} {false} {#3} }
+ }
+ }
+
+\NewExpandableDocumentCommand \GetExerciseParameter {m}
+ { \xsim_get_parameter:on {\ExerciseType} {#1} }
+
+\NewDocumentCommand \SetExerciseParameter {mmm}
+ { \xsim_set_parameter:nnn {#1} {#2} {#3} }
+
+\NewDocumentCommand \SetExerciseParameters {mm}
+ { \xsim_set_parameters:nn {#1} {#2} }
+
+\NewExpandableDocumentCommand \GetExerciseName {}
+ {
+ \IfInsideSolutionTF
+ { \GetExerciseParameter {solution-name} }
+ { \GetExerciseParameter {exercise-name} }
+ }
+
+\NewDocumentCommand \GetExerciseBody {m}
+ { \xsim_input:oon { \ExerciseType } { \ExerciseID } {#1} }
+
+\NewExpandableDocumentCommand \IfExerciseParameterSetTF {+m+m+m}
+ { \xsim_if_parameter_set:nnTF {\ExerciseType} {#1} {#2} {#3} }
+
+\NewExpandableDocumentCommand \IfExerciseParameterSetT {+m+m}
+ { \IfExerciseParameterSetTF {#1} {#2} {} }
+
+\NewExpandableDocumentCommand \IfExerciseParameterSetF {+m+m}
+ { \IfExerciseParameterSetTF {#1} {} {#2} }
+
+\NewExpandableDocumentCommand \ExerciseParameterIfSetTF {+m+m+m+m}
+ { \xsim_if_parameter_set:nnTF {#1} {#2} {#3} {#4} }
+
+\NewExpandableDocumentCommand \ExerciseParameterIfSetT {+m+m+m}
+ { \ExerciseParameterIfSetTF {#1} {#2} {#3} {} }
+
+\NewExpandableDocumentCommand \ExerciseParameterIfSetF {+m+m+m}
+ { \ExerciseParameterIfSetTF {#1} {#2} {} {#3} }
+
+\NewDocumentCommand \GetExerciseParameterTF {m+m+m}
+ { \xsim_get_parameter_if_set:onTF {\ExerciseType} {#1} {#2} {#3} }
+
+\NewDocumentCommand \GetExerciseParameterT {m+m}
+ { \GetExerciseParameterTF {#1} {#2} {} }
+
+\NewDocumentCommand \GetExerciseParameterF {m+m+m}
+ { \GetExerciseParameterTF {#1} {} {#2} }
+
+% ----------------------------------------------------------------------------
+
+\NewDocumentCommand \ExercisePropertyIfSetTF {mmm+m+m}
+ { \xsim_if_property_set:nnnTF {#1} {#2} {#3} {#4} {#5} }
+
+\NewDocumentCommand \ExercisePropertyIfSetT {mmm+m}
+ { \ExercisePropertyIfSetTF {#1} {#2} {#3} {#4} {} }
+
+\NewDocumentCommand \ExercisePropertyIfSetF {mmm+m}
+ { \ExercisePropertyIfSetTF {#1} {#2} {#3} {} {#4} }
+
+\NewExpandableDocumentCommand \ExercisePropertyGet {mmm}
+ { \xsim_get_property:nnn {#1} {#2} {#3} }
+
+\NewExpandableDocumentCommand \ExercisePropertyGetAlias {mm}
+ { \xsim_get_property:nnf {#1} {#2} { \xsim_property_alias:n {#1} } }
+
+\NewDocumentCommand \ExercisePropertySave {mmmm}
+ { \xsim_save_property:nnnN {#1} {#2} {#3} #4 }
+
+\NewDocumentCommand \ExercisePropertyGlobalSave {mmmm}
+ { \xsim_gsave_property:nnnN {#1} {#2} {#3} #4 }
+
+\NewExpandableDocumentCommand \ExerciseParameterGet {mm}
+ { \xsim_get_parameter:nn {#1} {#2} }
+
+% ----------------------------------------------------------------------------
+
+\NewExpandableDocumentCommand \XSIMtranslate {m}
+ { \xsim_translate:n {#1} }
+
+\NewDocumentCommand \XSIMexpandcode {+m}
+ { \use:x {#1} }
+
+\NewExpandableDocumentCommand \XSIMmixedcase {m}
+ { \tl_mixed_case:f {#1} }
+
+\NewDocumentCommand \XSIMputright {mm}
+ { \tl_put_right:Nn #1 {#2} }
+
+\NewExpandableDocumentCommand \XSIMifeqTF {+m+m+m+m}
+ { \tl_if_eq:ffTF {#1} {#2} {#3} {#4} }
+
+\NewExpandableDocumentCommand \XSIMifeqT {+m+m+m}
+ { \XSIMifeqTF {#1} {#2} {#3} {} }
+
+\NewExpandableDocumentCommand \XSIMifeqF {+m+m+m}
+ { \XSIMifeqTF {#1} {#2} {} {#3} }
+
+\NewExpandableDocumentCommand \XSIMifblankTF {+m+m+m}
+ { \tl_if_blank:fTF {#1} {#2} {#3} }
+
+\NewExpandableDocumentCommand \XSIMifblankT {+m+m}
+ { \XSIMifblankTF {#1} {#2} {} }
+
+\NewExpandableDocumentCommand \XSIMifblankF {+m+m}
+ { \XSIMifblankTF {#1} {} {#2} {} }
+
+% ----------------------------------------------------------------------------
+
+\NewDocumentCommand \DeclareExerciseGoal {m}
+ { \xsim_declare_exercise_goal:n {#1} }
+\@onlypreamble \DeclareExerciseGoal
+
+\NewDocumentCommand \IfExerciseGoalTF {mm+m+m}
+ {
+ \xsim_if_goal_value:xxnnTF
+ {\ExerciseType} {\ExerciseID} {#1}
+ {#2}
+ {#3} {#4}
+ }
+
+\NewDocumentCommand \IfExerciseGoalT {mm+m}
+ { \IfExerciseGoalTF {#1} {#2} {#3} {} }
+
+\NewDocumentCommand \IfExerciseGoalF {mm+m}
+ { \IfExerciseGoalTF {#1} {#2} {} {#3} }
+
+\NewDocumentCommand \IfExerciseGoalSingularTF {mmm}
+ { \IfExerciseGoalTF {#1} {=1} {#2} {#3} }
+
+\NewDocumentCommand \IfExerciseGoalSingularT {mm}
+ { \IfExerciseGoalT {#1} {=1} {#2} }
+
+\NewDocumentCommand \IfExerciseGoalSingularF {mm}
+ { \IfExerciseGoalF {#1} {=1} {#2} }
+
+\NewDocumentCommand \IfExerciseTypeGoalsSumTF {mmm+m+m}
+ { \xsim_if_goals_sum:nnnTF {#1} {#2} {#3} {#4} {#5} }
+
+\NewDocumentCommand \IfExerciseTypeGoalsSumT {mmm+m}
+ { \IfExerciseTypeGoalsSumTF {#1} {#2} {#3} {#4} {} }
+
+\NewDocumentCommand \IfExerciseTypeGoalsSumF {mmm+m}
+ { \IfExerciseTypeGoalsSumTF {#1} {#2} {#3} {} {#4} }
+
+\NewDocumentCommand \IfExerciseGoalsSumTF {mm+m+m}
+ { \xsim_if_total_goals_sum:nnTF {#1} {#2} {#3} {#4} }
+
+\NewDocumentCommand \IfExerciseGoalsSumT {mm+m}
+ { \IfExerciseGoalsSumTF {#1} {#2} {#3} {} }
+
+\NewDocumentCommand \IfExerciseGoalsSumF {mm+m}
+ { \IfExerciseGoalsSumTF {#1} {#2} {} {#3} }
+
+\NewDocumentCommand \TotalExerciseTypeGoal {mmmm}
+ { \xsim_print_goal_sum:nnnn {#1} {#2} {#3} {#4} }
+
+\NewDocumentCommand \TotalExerciseTypeGoals {mmmm}
+ { \xsim_print_goals_sum:nnnn {#1} {#2} {#3} {#4} }
+
+\NewDocumentCommand \TotalExerciseGoal {mmm}
+ { \xsim_print_total_goal_sum:nnn {#1} {#2} {#3} }
+
+\NewDocumentCommand \TotalExerciseGoals {mmm}
+ { \xsim_print_total_goals_sum:nnn {#1} {#2} {#3} }
+
+\NewDocumentCommand \AddtoExerciseTypeGoal {mmmm}
+ { \xsim_addto_goal:nnnn {#1} {#2} {#3} {#4} }
+
+\NewDocumentCommand \AddtoExerciseGoal {mm}
+ { \xsim_addto_goal:xxnn {\ExerciseType} {\ExerciseID} {#1} {#2} }
+
+\NewDocumentCommand \ExerciseGoalValuePrint {mmm}
+ { \xsim_print_goal:nnn {#1} {#2} {#3} }
+
+\NewDocumentCommand \AddtoExerciseTypeGoalPrint {mmmmmm}
+ {
+ \xsim_addto_goal:nnnn {#1} {#2} {#3} {#4}
+ \xsim_print_goal:nnn {#4} {#5} {#6}
+ }
+
+\NewDocumentCommand \AddtoExerciseGoalPrint {mmmm}
+ {
+ \xsim_addto_goal:xxnn {\ExerciseType} {\ExerciseID} {#1} {#2}
+ \xsim_print_goal:nnn {#2} {#3} {#4}
+ }
+
+% ----------------------------------------------------------------------------
+\NewDocumentCommand \DeclareGradeDistribution {m}
+ { \xsim_declare_relative_grades:n {#1} }
+
+\NewDocumentCommand \GetGradeRequirementForGoal {mmmm}
+ { \xsim_get_grade_goal:nnnn {#1} {#2} {#3} {#4} }
+
+\NewDocumentCommand \GetGradeRequirementForGoals {mmmm}
+ { \xsim_get_grade_requirement:nnnn {#1} {#2} {#3} {#4} }
+
+\NewDocumentCommand \GetGradeRequirement {mmm}
+ { \xsim_get_absolute_grade_requirement:nnn {#1} {#2} {#3} }
+
+% ----------------------------------------------------------------------------
+
+\NewDocumentCommand \DeclareExerciseTagging {m}
+ { \xsim_new_tag_type:n {#1} }
+\@onlypreamble \DeclareExerciseTagging
+
+\NewDocumentCommand \ProvideExerciseTagging {m}
+ {
+ \xsim_if_tag_type_exists:nTF {#1}
+ { \msg_warning:nnn {xsim} {tag-exists} {#1} }
+ { \xsim_new_tag_type:n {#1} }
+ }
+
+\NewDocumentCommand \ForEachExerciseTag {m+m}
+ { \xsim_foreach_exercise_tag:oonn {\ExerciseType} {\ExerciseID} {#1} {#2} }
+
+\NewDocumentCommand \ListExerciseTags {mm}
+ { \xsim_exercise_tags_use:oonn {\ExerciseType} {\ExerciseID} {#1} {#2} }
+
+\NewDocumentCommand \UseExerciseTags {mmmm}
+ {
+ \xsim_exercise_tags_use:oonnnn
+ {\ExerciseType}
+ {\ExerciseID}
+ {#1}
+ {#2}
+ {#3}
+ {#4}
+ }
+
+\NewDocumentCommand \IfExerciseTagSetTF {m+m+m}
+ { \xsim_if_tags_value:nTF {#1} {#2} {#3} }
+\NewDocumentCommand \IfExerciseTagSetT {m+m}
+ { \xsim_if_tags_value:nT {#1} {#2} }
+\NewDocumentCommand \IfExerciseTagSetF {m+m}
+ { \xsim_if_tags_value:nF {#1} {#2} }
+
+\NewDocumentCommand \IfExerciseTopicSetTF {m+m+m}
+ { \xsim_if_topic_value:nTF {#1} {#2} {#3} }
+\NewDocumentCommand \IfExerciseTopicSetT {m+m}
+ { \xsim_if_topic_value:nT {#1} {#2} }
+\NewDocumentCommand \IfExerciseTopicSetF {m+m}
+ { \xsim_if_topic_value:nF {#1} {#2} }
+
+% ----------------------------------------------------------------------------
+
+\NewDocumentCommand \ForEachPrintedExerciseByType {+m}
+ { \xsim_foreach_exercise_type_id:nn {print} {#1} }
+
+\NewDocumentCommand \ForEachPrintedExerciseByID {+m}
+ { \xsim_foreach_exercise_id_type:nn {print} {#1} }
+
+\NewDocumentCommand \ForEachUsedExerciseByType {+m}
+ { \xsim_foreach_exercise_type_id:nn {use} {#1} }
+
+\NewDocumentCommand \ForEachUsedExerciseByID {+m}
+ { \xsim_foreach_exercise_id_type:nn {use} {#1} }
+
+% ----------------------------------------------------------------------------
+
+\NewDocumentCommand \DeclareExerciseTranslations {mm}
+ { \xsim_declare_translations:nn {#1} {#2} }
+\@onlypreamble \DeclareExerciseTranslations
+
+\NewDocumentCommand \DeclareExerciseTranslation {mmm}
+ { \xsim_declare_translation:nnn {#1} {#2} {#3} }
+\@onlypreamble \DeclareExerciseTranslation
+
+\NewDocumentCommand \ForEachExerciseTranslation {+m}
+ { \xsim_for_all_translations_do:n {#1} }
+
+% ----------------------------------------------------------------------------
+
+\NewDocumentCommand \DeclareExerciseCollection {m}
+ { \xsim_new_collection:n {#1} }
+\@onlypreamble \DeclareExerciseCollection
+
+\NewDocumentCommand \collectexercisestype {mm}
+ { \xsim_start_collection:nn {#1} {#2} }
+
+\NewDocumentCommand \collectexercises {m}
+ { \xsim_start_collection:n {#1} }
+
+\NewDocumentCommand \collectexercisesstop {m}
+ { \xsim_stop_collection:n {#1} }
+
+\NewDocumentCommand \printcollection {O{}m}
+ { \xsim_print_collection:nn {#1} {#2} }
+
+\NewDocumentCommand \printrandomexercises {O{}m}
+ {
+ \group_begin:
+ \keys_set:nn {xsim/random} {#1}
+ \xsim_print_random_list:VnV
+ \l_xsim_random_collection_tl
+ {#2}
+ \l_xsim_random_exclude_list_tl
+ \group_end:
+ }
+
+% ----------------------------------------------------------------------------
+
+\NewDocumentCommand \blank {som}
+ {
+ \group_begin:
+ \IfNoValueF {#2} { \keys_set:nn {xsim/blank} {#2} }
+ \mode_if_vertical:TF
+ {
+ \IfBooleanTF {#1}
+ { \xsim_blank:n {#3} }
+ {
+ \bool_if:NT \l__xsim_blank_linespread_bool
+ { \linespread { \l__xsim_blank_linespread_tl } \selectfont }
+ \noindent \null \xsim_blank:n {#3} \par
+ }
+ }
+ { \xsim_blank:n {#3} }
+ \group_end:
+ }
+
+\NewDocumentCommand \examspace { sm }
+ {
+ \IfBooleanTF {#1}
+ { \xsim_examspace:nn { \c_true_bool } {#2} }
+ { \xsim_examspace:nn { \c_false_bool } {#2} }
+ }
+
+% ----------------------------------------------------------------------------
+
+\NewDocumentCommand \gradingtable {O{}}
+ {
+ \group_begin:
+ \keys_set:nn {xsim/grading-table} {#1}
+ \xsim_setup_template:nVn {table} \l_xism_grading_table_template_tl
+ {
+ \providecommand* \ExerciseTableCode {}
+ \providecommand* \ExerciseTableType [1] {}
+ \providecommand* \ExerciseType {}
+ \tl_set_eq:NN \ExerciseType \l_xism_grading_table_exercise_type_tl
+ \renewcommand* \ExerciseTableCode {}
+ \renewcommand* \ExerciseTableType [1]
+ { \XSIMifblankTF { \ExerciseType } {##1} { \ExerciseType } }
+ }
+ \xsim_use_template:nV {table} \l_xism_grading_table_template_tl
+ \group_end:
+ }
+
+% ----------------------------------------------------------------------------
+
+\NewDocumentCommand \printsolution {O{}mm}
+ { \xsim_insert:nnnn {#2} {#3} {#1} {solution} }
+
+\NewDocumentCommand \printexercise {O{}mm}
+ { \xsim_insert:nnnn {#2} {#3} {#1} {exercise} }
+
+% ----------------------------------------------------------------------------
+
+\NewDocumentCommand \printsolutionstype {sO{}m}
+ {
+ \IfBooleanTF {#1}
+ { \xsim_print_type_solutions:nnn { \c_true_bool } {#2} {#3} }
+ { \xsim_print_type_solutions:nnn { \c_false_bool } {#2} {#3} }
+ }
+
+\NewDocumentCommand \printsolutions {sO{}}
+ {
+ \IfBooleanTF {#1}
+ { \xsim_print_all_solutions_per_type:nn { \c_true_bool } {#2} }
+ { \xsim_print_all_solutions_per_type:nn { \c_false_bool } {#2} }
+ }
+
+\NewDocumentCommand \printallsolutions {sO{}}
+ {
+ \IfBooleanTF {#1}
+ { \xsim_print_all_solutions_per_id:nn { \c_true_bool } {#2} }
+ { \xsim_print_all_solutions_per_id:nn { \c_false_bool } {#2} }
+ }
+
+% ----------------------------------------------------------------------------
+
+\NewDocumentCommand \printforexercises {sO{}+mO{}}
+ {
+ \IfBooleanTF {#1}
+ {
+ \xsim_foreach_exercise_type:n
+ { \xsim_print_type_code:nnnnn { \c_true_bool } {#2} {#4} {##1} {#3} }
+ }
+ {
+ \xsim_foreach_exercise_type:n
+ { \xsim_print_type_code:nnnnn { \c_false_bool } {#2} {#4} {##1} {#3} }
+ }
+ }
+
+% ----------------------------------------------------------------------------
+
+\NewDocumentCommand \xsimsetup {m}
+ { \xsim_setup:n {#1} }
+
+% ----------------------------------------------------------------------------
+\NewExpandableDocumentCommand \XSIMifchapterTF {+m+m}
+ { \xsim_if_chapter:TF {#1} {#2} }
+
+\NewExpandableDocumentCommand \XSIMifchapterT {+m}
+ { \xsim_if_chapter:T {#1} }
+
+\NewExpandableDocumentCommand \XSIMifchapterF {+m}
+ { \xsim_if_chapter:F {#1} }
+
+% ----------------------------------------------------------------------------
+\file_input_stop:
diff --git a/macros/latex/contrib/xsim/code/xsim.modules.code.tex b/macros/latex/contrib/xsim/code/xsim.modules.code.tex
new file mode 100644
index 0000000000..9337dc2073
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsim.modules.code.tex
@@ -0,0 +1,212 @@
+% ----------------------------------------------------------------------------
+% the XSIM package - modules module
+%
+% eXercise Sheets IMproved
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+%
+% this is a pseudo module as it does the actual definitions for the whole
+% module loading stuff; we do this so both packages `xsimverb' and `xsim' can
+% use the `verbwrite' module
+\RequirePackage{scrlfile}
+\ExplSyntaxOn
+
+\msg_new:nnn {xsim} {module-missing}
+ {
+ You've~ requested~ the~ xsim~ module~ `#1'~ but~ it~ appears~ to~
+ be~ missing~ on~ your~ system.~ Maybe~ you've~ misspelled~ the~ name?~
+ Loading~ the~ module~ will~ abort~ \msg_line_context:
+ }
+
+\msg_new:nnn {xsim} {loading-module}
+ { Loading~ module~ `#1'~ ... }
+
+\msg_new:nnn {xsim} {wrong-module-name}
+ { The~ module~ file~ name~ is~ `#2'~ but~ it~ provides~ module~ `#1'. }
+
+\msg_new:nnn {xsim} {forbidden-module}
+ {
+ You've~ requested~ the~ xsim~ module~ `modules'~ \msg_line_context: .~
+ This~ module~ is~ a~ pseudo~ module~ which~ may~ not~ be~ loaded~ through~
+ the~ module~ loading~ mechansim.
+ }
+
+% ----------------------------------------------------------------------------
+
+\tl_const:Nn \c__xsim_module_extension_tl {code.tex}
+\tl_const:Nn \c__xsim_module_prefix_tl {xsim}
+
+\seq_new:N \g__xsim_loaded_modules_seq
+\prop_new:N \g__xsim_loaded_modules_prop
+
+\cs_new_protected:Npn \xsim_load_modules:n #1
+ { \clist_map_inline:nn {#1} { \xsim_load_module:n {##1} } }
+
+\cs_new_protected:Npn \xsim_load_module:n #1
+ {
+ \tl_set:Nx \l_tmpa_tl { \tl_trim_spaces:n {#1} }
+ \str_if_eq_x:nnTF { \l_tmpa_tl } {modules}
+ { \msg_error:nn {xsim} {forbidden-module} }
+ { \__xsim_load_module:V \l_tmpa_tl }
+ }
+
+\cs_new_protected:Npn \__xsim_load_module:n #1
+ {
+ \xsim_if_module_loaded:nF {#1}
+ {
+ \xsim_if_module_exist:nTF {#1}
+ {
+ \__xsim_use_module_hook:nn {#1} {before}
+ \msg_info:nnn {xsim} {loading-module} {#1}
+ \@onefilewithoptions
+ {\c__xsim_module_prefix_tl.#1}[][]
+ \c__xsim_module_extension_tl
+ \__xsim_use_module_hook:nn {#1} {after}
+ }
+ { \msg_error:nnn {xsim} {module-missing} {#1} }
+ }
+ }
+\cs_generate_variant:Nn \__xsim_load_module:n { V }
+
+\prg_new_conditional:Npnn \xsim_if_module_loaded:n #1 {p,T,F,TF}
+ {
+ \seq_if_in:NnTF \g__xsim_loaded_modules_seq {#1}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+\prg_new_conditional:Npnn \xsim_if_module_exist:n #1 {p,T,F,TF}
+ {
+ \file_if_exist:nTF
+ {\c__xsim_module_prefix_tl.#1.\c__xsim_module_extension_tl}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+% --------------------------------------------------------------------------
+% module hooks:
+% #1: module
+% #2: code
+\cs_new_protected:Npn \xsim_after_module:nn #1#2
+ {
+ \xsim_if_module_loaded:nTF {#1}
+ {#2}
+ { \__xsim_add_to_module_hook:nnn {#1} {after} {#2} }
+ }
+
+% #1: module
+% #2: code
+\cs_new_protected:Npn \xsim_before_module:nn #1#2
+ {
+ \xsim_if_module_loaded:nF {#1}
+ { \__xsim_add_to_module_hook:nnn {#1} {before} {#2} }
+ }
+
+% #1: module
+% #2: hook name
+% #3: code
+\cs_new_protected:Npn \__xsim_add_to_module_hook:nnn #1#2#3
+ {
+ \xsim_if_module_loaded:nF {#1}
+ {
+ \tl_if_exist:cF {g__xsim_module_#1_#2_hook_tl}
+ { \tl_new:c {g__xsim_module_#1_#2_hook_tl} }
+ \tl_gput_right:cn {g__xsim_module_#1_#2_hook_tl} {#3}
+ }
+ }
+
+% #1: module
+% #2: hook name
+\cs_new_protected:Npn \__xsim_use_module_hook:nn #1#2
+ {
+ \tl_if_exist:cT {g__xsim_module_#1_#2_hook_tl}
+ {
+ \tl_use:c {g__xsim_module_#1_#2_hook_tl}
+ \tl_gclear:c {g__xsim_module_#1_#2_hook_tl}
+ }
+ }
+
+% --------------------------------------------------------------------------
+
+\tl_new:N \l__xsim_module_date_tl
+\tl_new:N \l__xsim_module_version_tl
+
+\tl_if_exist:NF \c_xsim_date_tl
+ { \tl_set:Nx \l__xsim_module_date_tl {\c_xsimverb_date_tl} }
+\tl_if_exist:NF \c_xsim_version_tl
+ { \tl_set:Nx \l__xsim_module_version_tl {\c_xsimverb_version_tl} }
+
+\AfterPackage*{xsim}
+ {
+ \tl_set:Nx \l__xsim_module_date_tl {\c_xsim_date_tl}
+ \tl_set:Nx \l__xsim_module_version_tl {\c_xsim_version_tl}
+ }
+
+\cs_new_protected:Npn \__xsim_module:nn #1#2
+ {
+ \seq_gput_right:Nn \g__xsim_loaded_modules_seq {#1}
+ \prop_gput:Nnn \g__xsim_loaded_modules_prop {#1} {#2}
+ \str_if_eq_x:nnF
+ {\c__xsim_module_prefix_tl.#1.\c__xsim_module_extension_tl}
+ {\@currname.\@currext}
+ {
+ \msg_error:nnnxx {xsim} {wrong-module-name}
+ {#1}
+ {\@currname.\@currext}
+ }
+ \ProvidesFile
+ {\c__xsim_module_prefix_tl.#1.\c__xsim_module_extension_tl}
+ [
+ \l__xsim_module_date_tl
+ \c_space_tl
+ v \l__xsim_module_version_tl
+ \c_space_tl
+ xsim~ module~ `#1'~ (#2)
+ ]
+ }
+\cs_generate_variant:Nn \msg_error:nnnn {nnnx}
+
+% --------------------------------------------------------------------------
+% define a xsim module:
+% #1: name
+% #2: description
+\NewDocumentCommand \XSIMmodule {mm}
+ { \__xsim_module:nn {#1} {#2} \ExplSyntaxOn }
+
+% --------------------------------------------------------------------------
+% style files
+\NewDocumentCommand \xsimstyle {smO{}}
+ {
+ \__xsim_module:nn {style.#2} {#3}
+ \IfBooleanT {#1} { \ExplSyntaxOn }
+ }
+
+\cs_new_protected:Npn \xsim_style_options:nn #1#2
+ { \keys_define:nn {xsim/#1} {#2} }
+
+\NewDocumentCommand \loadxsimstyle {m}
+ { \clist_map_inline:nn {#1} { \xsim_load_module:n {style.##1} } }
+
+% --------------------------------------------------------------------------
+\file_input_stop:
diff --git a/macros/latex/contrib/xsim/code/xsim.properties.code.tex b/macros/latex/contrib/xsim/code/xsim.properties.code.tex
new file mode 100644
index 0000000000..06558868cb
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsim.properties.code.tex
@@ -0,0 +1,496 @@
+% ----------------------------------------------------------------------------
+% the XSIM package - properties module
+%
+% eXercise Sheets IMproved
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+\XSIMmodule{properties}{properties of exercises}
+
+\xsim_load_modules:n {base,auxfile}
+
+\msg_new:nnn {xsim} {unknown-property}
+ {
+ You~ tried~ to~ set~ the~ property~ `#1'~ \msg_line_context: . \\
+ This~ property~ does~ not~ exist.~ Check~ for~ a~ typo~ or~ \\
+ define~ the~ property~ yourself.
+ }
+
+\msg_new:nnn {xsim} {property-unique}
+ {
+ You~ tried~ to~ set~ the~ property~ `#1'~ \msg_line_context: . \\
+ This~ property~ has~ to~ have~ a~ unique~ value~ and~ thus~ \\
+ cannot~ be~ set~ to~ `#2'~ since~ this~ value~ is~ already~ talen.
+ }
+
+\msg_new:nnn {xsim} {no-boolean-property}
+ {
+ You~ tried~ to~ check~ the~ boolean~ value~ of~ property~ `#1'~
+ \msg_line_context: . \\
+ However,~ property~ `#1'~ is~ not~ a~ boolean~ property!
+ }
+
+\seq_new:N \l__xsim_properties_seq
+\seq_new:N \l__xsim_boolean_properties_seq
+\seq_new:N \l__xsim_unique_properties_seq
+\seq_new:N \l__xsim_noupdate_properties_seq
+\prop_new:N \g__xsim_properties_prop
+
+% new property:
+% #1: boolean if unique
+% #2: boolean if boolean
+% #3: boolean if noupdate
+% #4: property name
+\cs_new_protected:Npn \xsim_declare_property:nnnn #1#2#3#4
+ {
+ \xsim_if_property_exist:nF {#4}
+ {
+ \seq_put_right:Nn \l__xsim_properties_seq {#4}
+ \bool_if:nTF {#3}
+ {
+ \seq_if_in:NnF \l__xsim_noupdate_properties_seq {#4}
+ { \seq_put_right:Nn \l__xsim_noupdate_properties_seq {#4} }
+ }
+ { \xsim_new_list:n {#4} }
+ \bool_if:nTF {#2}
+ {
+ \xsim_verbose:n
+ { Declaring~ new~ boolean~ exercise~ property~ `#4'. }
+ \seq_if_in:NnF \l__xsim_boolean_properties_seq {#4}
+ { \seq_put_right:Nn \l__xsim_boolean_properties_seq {#4} }
+ }
+ {
+ \bool_if:nTF {#1}
+ {
+ \xsim_verbose:n
+ { Declaring~ new~ unique~ exercise~ property~ `#4'. }
+ \seq_if_in:NnF \l__xsim_unique_properties_seq {#4}
+ { \seq_put_right:Nn \l__xsim_unique_properties_seq {#4} }
+ \seq_new:c {l__xsim_unique_property_#4_values_seq}
+ \prop_new:c {g__xsim_property_#4_type_prop}
+ \prop_new:c {g__xsim_property_#4_id_prop}
+ }
+ { \xsim_verbose:n { Declaring~ new~ exercise~ property~ `#4'. } }
+ }
+ \bool_if:nF {#3}
+ {
+ \AtEndDocument { \xsim_update_list:n {#4} }
+ \AtBeginDocument { \__xsim_set_property_from_list:n {#4} }
+ }
+ }
+ }
+
+% check if property exists:
+\prg_new_conditional:Npnn \xsim_if_property_exist:n #1 {p,T,F,TF}
+ {
+ \seq_if_in:NnTF \l__xsim_properties_seq {#1}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\cs_generate_variant:Nn \xsim_if_property_exist:nTF {x}
+
+\prg_new_conditional:Npnn \xsim_if_property_noupdate:n #1 {p,T,F,TF}
+ {
+ \seq_if_in:NnTF \l__xsim_noupdate_properties_seq {#1}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\cs_generate_variant:Nn \xsim_if_property_noupdate:nF {V}
+
+\prg_new_conditional:Npnn \xsim_if_property_boolean:n #1 {p,T,F,TF}
+ {
+ \seq_if_in:NnTF \l__xsim_boolean_properties_seq {#1}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+\prg_new_conditional:Npnn \xsim_if_property_unique:n #1 {p,T,F,TF}
+ {
+ \seq_if_in:NnTF \l__xsim_unique_properties_seq {#1}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+% #1: property
+% #2: value
+\prg_new_conditional:Npnn \xsim_if_property_unique_value:nn #1#2 {p,T,F,TF}
+ {
+ \xsim_if_property_unique:nTF {#1}
+ {
+ \seq_if_in:cnTF {l__xsim_unique_property_#1_values_seq} {#2}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+ { \prg_return_false: }
+ }
+
+% #1: type
+% #2: id
+% #3: property
+% #4: value
+\cs_new_protected:Npn \xsim_set_property:nnnn #1#2#3#4
+ {
+ \xsim_if_property_exist:nTF {#3}
+ {
+ \xsim_if_property_set:nnnTF {#1} {#2} {#3}
+ {
+ \xsim_if_property_unique_value:nnTF {#3} {#4}
+ { \msg_warning:nnnn {xsim} {property-unique} {#3} {#4} }
+ {
+ \xsim_if_property_boolean:nTF {#3}
+ { \__xsim_set_boolean_property:nnnn {#1} {#2} {#3} {#4} }
+ {
+ \xsim_if_property_unique:nTF {#3}
+ { \__xsim_set_unique_property:nnnn {#1} {#2} {#3} {#4} }
+ { \__xsim_set_property:nnnn {#1} {#2} {#3} {#4} }
+ }
+ }
+ }
+ {
+ \xsim_if_property_boolean:nTF {#3}
+ { \__xsim_set_boolean_property:nnnn {#1} {#2} {#3} {#4} }
+ {
+ \xsim_if_property_unique:nTF {#3}
+ { \__xsim_set_unique_property:nnnn {#1} {#2} {#3} {#4} }
+ { \__xsim_set_property:nnnn {#1} {#2} {#3} {#4} }
+ }
+ }
+ }
+ { \msg_error:nnn {xsim} {unknown-property} {#3} }
+ }
+\cs_generate_variant:Nn \xsim_set_property:nnnn {nnxx,nnnV,nV,nVnx,nnnx,xx,xxnx}
+
+\cs_new_protected:Npn \__xsim_set_boolean_property:nnnn #1#2#3#4
+ {
+ \str_case_x:nnTF { \str_lower_case:n {#4} }
+ {
+ {} {}
+ {true} {}
+ }
+ { \__xsim_set_property:nnnn {#1} {#2} {#3} {true} }
+ { \__xsim_set_property:nnnn {#1} {#2} {#3} {false} }
+ }
+
+\cs_new_protected:Npn \__xsim_set_unique_property:nnnn #1#2#3#4
+ {
+ \__xsim_set_property:nnnn {#1} {#2} {#3} {#4}
+ \__xsim_set_property_reverse:nnnn {#1} {#2} {#3} {#4}
+ }
+
+% #1: type
+% #2: id
+% #3: property
+\cs_new_protected:Npn \xsim_unset_property:nnn #1#2#3
+ {
+ \xsim_if_property_exist:nTF {#3}
+ { \__xsim_unset_property:nnn {#1} {#2} {#3} }
+ { \msg_error:nnn {xsim} {unknown-property} {#3} }
+ }
+\cs_generate_variant:Nn \xsim_unset_property:nnn {nx,nnx,nV}
+
+% #1: type
+% #2: id
+% #3: property
+% #4: value
+\cs_new_protected:Npn \__xsim_set_property:nnnn #1#2#3#4
+ {
+ \xsim_verbose:n
+ {
+ Setting~ property~ `#3'~ of~ exercise~ type~ `#1'~ id~ `#2'~ to~
+ value~ `#4'.
+ }
+ \__xsim_remove_property_from_list:nnn {#1} {#2} {#3}
+ % now we can safely add the property to the list:
+ \xsim_add_to_list:nn {#3} {#1-#2=={#4}}
+ \prop_gput:Nnn \g__xsim_properties_prop {#2!#1!#3} {#4}
+ }
+\cs_generate_variant:Nn \__xsim_set_property:nnnn {VVnV}
+
+% #1: type
+% #2: id
+% #3: property
+% #4: value
+\cs_new_protected:Npn \__xsim_set_property_reverse:nnnn #1#2#3#4
+ {
+ \prop_gput:cnn {g__xsim_property_#3_type_prop} {#4} {#1}
+ \prop_gput:cnn {g__xsim_property_#3_id_prop} {#4} {#2}
+ }
+
+% #1: type
+% #2: id
+% #3: property
+\cs_new_protected:Npn \__xsim_unset_property:nnn #1#2#3
+ {
+ \xsim_verbose:n
+ { Un-setting~ property~ `#3'~ of~ exercise~ type~ `#1'~ id~ `#2'. }
+ \__xsim_remove_property_from_list:nnn {#1} {#2} {#3}
+ \prop_gremove:Nn \g__xsim_properties_prop {#2!#1!#3}
+ }
+
+\tl_set:Nx \l__xsim_tmpa_tl { \cs_to_str:N \{ }
+\tl_set:Nx \l__xsim_tmpb_tl { \cs_to_str:N \} }
+\use:x
+ {
+ \cs_new_protected:Npn
+ \exp_not:N \__xsim_extract_property_list_entry:NNNwww
+ ##1##2##3##4-##5== \l__xsim_tmpa_tl ##6 \l__xsim_tmpb_tl
+ \exp_not:N \q_stop
+ }
+ {
+ \str_set:Nn #1 {#4}
+ \str_set:Nn #2 {#5}
+ \str_set:Nn #3 {#6}
+ }
+
+\cs_new_protected:Npn \__xsim_remove_property_from_list:nnn #1#2#3
+ {
+ % make sure we change the entry if it has been recorded previously:
+ \xsim_foreach_list_entry:nn {#3}
+ {
+ \__xsim_extract_property_list_entry:NNNwww
+ \l__xsim_tmpa_str \l__xsim_tmpb_str \l__xsim_tmpc_str ##1 \q_stop
+ \str_if_eq:xnT { \l__xsim_tmpa_str - \l__xsim_tmpb_str } {#1-#2}
+ {
+ \xsim_remove_from_list:nx {#3}
+ { #1-#2 == {\l__xsim_tmpc_str} }
+ }
+ }
+ % make sure we change the entry if it has been set during the current run:
+ \xsim_foreach_new_list_entry:nn {#3}
+ {
+ \__xsim_extract_property_list_entry:NNNwww
+ \l__xsim_tmpa_str \l__xsim_tmpb_str \l__xsim_tmpc_str ##1 \q_stop
+ \str_if_eq:xnT { \l__xsim_tmpa_str - \l__xsim_tmpb_str } {#1-#2}
+ {
+ \xsim_remove_from_list:nx {#3}
+ { #1-#2 == {\l__xsim_tmpc_str} }
+ }
+ }
+ }
+
+\cs_new_protected:Npn \__xsim_set_property_from_list:n #1
+ {
+ \xsim_foreach_list_entry:nn {#1}
+ {
+ \__xsim_extract_property_list_entry:NNNwww
+ \l__xsim_tmpa_str \l__xsim_tmpb_str \l__xsim_tmpc_str ##1 \q_stop
+ \tl_set_rescan:Nnx \l__xsim_tmpc_tl {} { \l__xsim_tmpc_str }
+ \__xsim_set_property:VVnV
+ \l__xsim_tmpa_str
+ \l__xsim_tmpb_str
+ {#1}
+ \l__xsim_tmpc_tl
+ }
+ }
+
+% set properties:
+% #1: type
+% #2: id
+% #3: csv list of properties
+\cs_new_protected:Npn \xsim_set_properties:nnn #1#2#3
+ {
+ \clist_map_inline:nn {#3}
+ {
+ \seq_set_split:Nnx \l__xsim_tmpa_seq {=} { \tl_trim_spaces:n {##1} }
+ \xsim_set_property:nnxx
+ {#1}
+ {#2}
+ { \seq_item:Nn \l__xsim_tmpa_seq {1} }
+ { \seq_item:Nn \l__xsim_tmpa_seq {2} }
+ }
+ \xsim_property_set_aliases:nn {#1} {#2}
+ }
+\cs_generate_variant:Nn \xsim_set_properties:nnn {nnx,nxx,nV,nVx}
+
+% check if property is set:
+% #1: type
+% #2: id
+% #3: property
+\prg_new_protected_conditional:Npnn \xsim_if_property_set:nnn #1#2#3 {T,F,TF}
+ {
+ \prop_if_in:NxTF \g__xsim_properties_prop
+ { #2 ! #1 ! #3 }
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\cs_generate_variant:Nn \xsim_if_property_set:nnnTF {nnx,xx,nV}
+\cs_generate_variant:Nn \xsim_if_property_set:nnnF {nx,nnx,xx}
+\cs_generate_variant:Nn \xsim_if_property_set:nnnT {nnx,xx}
+
+% ----------------------------------------------------------------------------
+
+\prop_new:N \g__xsim_property_aliases_prop
+\seq_new:N \g__xsim_property_aliases_seq
+
+% #1: property
+\prg_new_conditional:Npnn \xsim_if_property_alias:n #1 {p,T,F,TF}
+ {
+ \prop_if_in:NnTF \g__xsim_property_aliases_prop {#1}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+% #1: property
+\cs_new:Npn \xsim_property_alias:n #1
+ { \prop_item:Nn \g__xsim_property_aliases_prop {#1} }
+
+% #1: property
+% #2: alias of
+\cs_new_protected:Npn \xsim_make_property_alias:nn #1#2
+ {
+ \xsim_if_property_alias:nF {#1}
+ {
+ \xsim_verbose:n
+ { Making~ property~ `#1'~ an~ alias~ of~ property~ `#2'. }
+ \prop_new:c {g__xsim_property_#1_alias_prop}
+ \prop_gput:Nnn \g__xsim_property_aliases_prop {#1} {#2}
+ \seq_gput_right:Nn \g__xsim_property_aliases_seq {#1}
+ }
+ }
+
+% #1: type
+% #2: id
+% #3: property
+\cs_new_protected:Npn \xsim_property_set_alias:nnn #1#2#3
+ {
+ \xsim_if_property_set:nnnF {#1} {#2} {#3}
+ {
+ \xsim_if_property_set:nnxT {#1} {#2}
+ { \xsim_property_alias:n {#3} }
+ {
+ \xsim_set_property:nnnx {#1} {#2} {#3}
+ {
+ \xsim_get_property:nnf {#1} {#2}
+ { \xsim_property_alias:n {#3} }
+ }
+ }
+ }
+ \xsim_if_property_set:nnxT {#1} {#2}
+ { \xsim_property_alias:n {#3} }
+ {
+ \prop_gput:cxx {g__xsim_property_#3_alias_prop}
+ { \xsim_get_property:nnn {#1} {#2} {#3} }
+ {
+ \xsim_get_property:nnf {#1} {#2}
+ { \xsim_property_alias:n {#3} }
+ }
+ }
+ }
+\cs_generate_variant:Nn \xsim_property_set_alias:nnn {nV}
+
+% #1: type
+% #2: id
+\cs_new_protected:Npn \xsim_property_set_aliases:nn #1#2
+ {
+ \seq_map_inline:Nn \g__xsim_property_aliases_seq
+ { \xsim_property_set_alias:nnn {#1} {#2} {##1} }
+ }
+
+% ----------------------------------------------------------------------------
+
+% retrieve properties:
+% #1: type
+% #2: id
+% #3: property
+\cs_new:Npn \xsim_get_property:nnn #1#2#3
+ { \prop_item:Nn \g__xsim_properties_prop { #2 ! #1 ! #3 } }
+\cs_generate_variant:Nn \xsim_get_property:nnn {nx,xx,oo,no,nnf,nf,oof,nV}
+
+% #1: property
+% #2: value
+\cs_new:Npn \xsim_get_id_for_property:nn #1#2
+ { \prop_item:cn {g__xsim_property_#1_id_prop} {#2} }
+
+% #1: property
+% #2: value
+\cs_new:Npn \xsim_get_type_for_property:nn #1#2
+ { \prop_item:cn {g__xsim_property_#1_type_prop} {#2} }
+\cs_generate_variant:Nn \xsim_get_type_for_property:nn {nV}
+
+\cs_new:Npn \__xsim_get_property_tmp:n #1 {}
+\cs_generate_variant:Nn \__xsim_get_property_tmp:n {V}
+
+% #1: type
+% #2: id
+% #3: property
+\cs_new_protected:Npn \xsim_get_property_if_set:nnnTF #1#2#3#4#5
+ {
+ \group_begin:
+ \xsim_if_property_exist:nTF {#3}
+ {
+ \xsim_if_property_set:nnnTF {#1} {#2} {#3}
+ {
+ \tl_set:Nx \PropertyValue
+ { \xsim_get_property:nnn {#1} {#2} {#3} }
+ \cs_set:Npn \__xsim_get_property_tmp:n ##1 {#4}
+ \__xsim_get_property_tmp:V \PropertyValue
+ }
+ {#5}
+ }
+ { \msg_error:nnn {xsim} {unknown-property} {#3} }
+ \group_end:
+ }
+\cs_new_protected:Npn \xsim_get_property_if_set:nnnT #1#2#3#4
+ { \xsim_get_property_if_set:nnnTF {#1} {#2} {#3} {#4} {} }
+\cs_new_protected:Npn \xsim_get_property_if_set:nnnF #1#2#3#4
+ { \xsim_get_property_if_set:nnnTF {#1} {#2} {#3} {} {#4} }
+\cs_generate_variant:Nn \xsim_get_property_if_set:nnnTF {oo}
+
+% #1: type
+% #2: id
+% #3: property
+\prg_new_conditional:Npnn \xsim_if_boolean_property:nnn #1#2#3 {T,F,TF}
+ {
+ \xsim_if_property_exist:nTF {#3}
+ {
+ \xsim_if_property_boolean:nTF {#3}
+ {
+ \xsim_if_property_set:nnnTF {#1} {#2} {#3}
+ {
+ \bool_if:cTF
+ {c_ \xsim_get_property:nnn {#1} {#2} {#3} _bool}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+ { \prg_return_false: }
+ }
+ { \msg_error:nnn {xsim} {no-boolean-property} {#3} }
+ }
+ { \msg_error:nnn {xsim} {unknown-property} {#3} }
+ }
+\cs_generate_variant:Nn \xsim_if_boolean_property:nnnTF {oo}
+\cs_generate_variant:Nn \xsim_if_boolean_property:nnnF {nV}
+
+\cs_new_protected:Npn \xsim_save_property:nnnN #1#2#3#4
+ { \prop_get:NnN \g__xsim_properties_prop { #2 ! #1 ! #3 } #4 }
+\cs_generate_variant:Nn \xsim_save_property:nnnN {nx,xx}
+
+\cs_new_protected:Npn \xsim_gsave_property:nnnN #1#2#3#4
+ {
+ \xsim_save_property:nnnN {#1} {#2} {#3} #4
+ \tl_gset:NV #4 #4
+ }
+\cs_generate_variant:Nn \xsim_gsave_property:nnnN {nx,xx}
+
+% ----------------------------------------------------------------------------
+\file_input_stop:
diff --git a/macros/latex/contrib/xsim/code/xsim.random.code.tex b/macros/latex/contrib/xsim/code/xsim.random.code.tex
new file mode 100644
index 0000000000..e8bddf2a10
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsim.random.code.tex
@@ -0,0 +1,317 @@
+% ----------------------------------------------------------------------------
+% the XSIM package - random module
+%
+% eXercise Sheets IMproved
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+\XSIMmodule{random}{randomly select exercises from collections}
+
+\msg_new:nnn {xsim} {random-numbers-unavailable}
+ {
+ You~ are~ compiling~ your~ document~ using ~XeLaTeX.~ Please~ be~ aware~
+ that~ random~ selection~ of~ exercises~ is~ unavailable~ in~ XeLaTeX.~ If~
+ you~ use~ this~ feature~ you~ can~ safely~ ignore~ this~ message.
+ }
+
+\sys_if_engine_xetex:T
+ { \msg_warning:nn {xsim} {random-numbers-unavailable} }
+
+\xsim_load_modules:n {collections}
+
+\tl_new:N \l_xsim_random_collection_tl
+\tl_new:N \l_xsim_random_exclude_list_tl
+\tl_new:N \l__xsim_print_random_choice_tl
+\tl_const:Nn \c__xsim_print_random_both_tl {both}
+\tl_const:Nn \c__xsim_print_random_exercise_tl {exercise}
+\tl_const:Nn \c__xsim_print_random_solution_tl {solution}
+\seq_new:N \g__xsim_random_list_seq
+\seq_new:N \l__xsim_allowed_exercise_ids_seq
+\int_new:N \g__xsim_random_list_id_int
+\bool_new:N \l__xsim_sort_random_bool
+
+% #1: random list id
+% #2: csv list of integers
+\xsim_new_aux_cs:cpn {random} #1#2
+ {
+ \xsim_random_list_if_exist:nF {#1}
+ { \xsim_new_random_list:n {#1} }
+ \xsim_set_random_list:nn {#1} {#2}
+ }
+
+\cs_new_protected:Npn \xsim_new_random_list:n #1
+ {
+ \xsim_verbose:n {Defining~ new~ random~ list~ `#1'}
+ \seq_new:c {g__xsim_random_#1 _seq}
+ }
+
+\cs_new_protected:Npn \xsim_set_random_list:nn #1#2
+ { \seq_gset_from_clist:cn {g__xsim_random_#1_seq} {#2} }
+
+\cs_new_protected:Npn \xsim_set_random_list_from_seq:nN #1#2
+ { \seq_gset_eq:cN {g__xsim_random_#1_seq} #2 }
+
+\cs_new:Npn \xsim_use_random_list:nn #1#2
+ { \seq_use:cn {g__xsim_random_#1_seq} {#2} }
+
+\cs_new_protected:Npn \xsim_map_random_list:nn #1#2
+ { \seq_map_inline:cn {g__xsim_random_#1_seq} {#2} }
+
+\prg_new_conditional:Npnn \xsim_random_list_if_exist:n #1 {p,T,F,TF}
+ {
+ \seq_if_exist:cTF {g__xsim_random_#1_seq}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+\prg_new_conditional:Npnn \xsim_random_list_if_empty:n #1 {p,T,F,TF}
+ {
+ \seq_if_empty:cTF {g__xsim_random_#1_seq}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+% #1: random list id
+% #2: sequence variable to set list from
+\cs_new_protected:Npn \xsim_save_random_list:nN #1#2
+ {
+ \xsim_random_list_if_exist:nF {#1}
+ { \xsim_new_random_list:n {#1} }
+ \xsim_random_list_if_empty:nT {#1}
+ { \xsim_set_random_list_from_seq:nN {#1} #2 }
+ \xsim_verbose:n {Saving~ random~ list~ `#1'}
+ \xsim_add_cs_to_aux:nn {random}
+ { {#1} { \xsim_use_random_list:nn {#1} {,} } }
+ }
+
+% #1: collection
+% #2: number of items
+% #3: csv list of excluded ids
+\cs_new_protected:Npn \xsim_print_random_list:nnn #1#2#3
+ {
+ \xsim_if_collection_exists:nTF {#1}
+ {
+ \int_gincr:N \g__xsim_random_list_id_int
+ \int_compare:nNnTF { \xsim_collection_count:n {#1} } = {0}
+ { \xsim_rerun: }
+ {
+ % generate seq of allowed ids from collection:
+ \xsim_foreach_exercise_id:n
+ {
+ \tl_set:Nx \l__xsim_tmpa_tl
+ { \xsim_get_type_for_property:nn {id} {##1} }
+ \xsim_if_in_collection:nVnT {#1} \l__xsim_tmpa_tl {##1}
+ {
+ \clist_if_in:nnF {#3} {##1}
+ { \seq_put_right:Nn \l__xsim_allowed_exercise_ids_seq {##1} }
+ }
+ }
+ \int_compare:nNnTF
+ {#2}
+ >
+ { \seq_count:N \l__xsim_allowed_exercise_ids_seq }
+ {
+ \int_set:Nn \l__xsim_tmpa_int
+ { \seq_count:N \l__xsim_allowed_exercise_ids_seq }
+ }
+ { \int_set:Nn \l__xsim_tmpa_int {#2} }
+ % generate random list:
+ \xsim_generate_random_list:nnnN
+ { \int_to_alph:n { \g__xsim_random_list_id_int } }
+ { \seq_count:N \l__xsim_allowed_exercise_ids_seq }
+ { \l__xsim_tmpa_int }
+ \g__xsim_random_list_seq
+ }
+ \xsim_save_random_list:nN
+ { \int_to_alph:n { \g__xsim_random_list_id_int } }
+ \g__xsim_random_list_seq
+ \xsim_verbose:x
+ {
+ Printing~ exercises~ of~ random~ list~
+ `\int_to_alph:n { \g__xsim_random_list_id_int }'~ of~
+ collection~ `#1'
+ }
+ \xsim_print_random_exercises:Nn
+ \l__xsim_allowed_exercise_ids_seq
+ { \int_to_alph:n { \g__xsim_random_list_id_int } }
+ }
+ { \msg_error:nnn {xsim} {unknown-collection} {#1} }
+ }
+\cs_generate_variant:Nn \xsim_print_random_list:nnn {VnV}
+
+% #1: random list id
+% #2: max number to choose from
+% #3: number of items
+% #4: seq variable
+\cs_new_protected:Npn \xsim_generate_random_list:nnnN #1#2#3#4
+ {
+ \bool_if:nT
+ {
+ !\xsim_random_list_if_exist_p:n {#1}
+ ||
+ \xsim_random_list_if_empty_p:n {#1}
+ }
+ {
+ \xsim_verbose:n
+ {Generating~ random~ list~ `#1'~ with~ #3~ out~ of~ #2~ items}
+ \seq_gclear:N #4
+ \int_do_until:nNnn { \seq_count:N #4 } = {#3}
+ {
+ \tl_set:Nx \l__xsim_tmpa_tl
+ { \fp_eval:n { randint(#2) } }
+ \seq_if_in:NVF #4
+ \l__xsim_tmpa_tl
+ { \seq_gput_right:NV #4 \l__xsim_tmpa_tl }
+ }
+ \bool_if:NT \l__xsim_sort_random_bool
+ {
+ \seq_sort:Nn #4
+ {
+ \int_compare:nNnTF {##1} > {##2}
+ { \sort_return_swapped: }
+ { \sort_return_same: }
+ }
+ }
+ }
+ }
+
+% #1: seq variable of ids
+% #2: random list id
+\cs_new_protected:Npn \xsim_print_random_exercises:Nn #1#2
+ {
+ \bool_if:nT
+ {
+ \xsim_random_list_if_exist_p:n {#2}
+ &&
+ !\xsim_random_list_if_empty_p:n {#2}
+ }
+ {
+ \xsim_verbose:x
+ {
+ Printing~
+ \l__xsim_print_random_choice_tl \c_space_tl
+ \str_if_eq:onT { \l__xsim_print_random_choice_tl } {both}
+ { exercises~ and~ solutions~ }
+ of~ random~ list~ `#2'
+ }
+ \xsim_map_random_list:nn {#2}
+ {
+ \tl_set:Nx \l__xsim_tmpa_tl
+ { \seq_item:Nn #1 {##1} }
+ \tl_set:Nx \l__xsim_tmpb_tl
+ { \xsim_get_type_for_property:nV {id} \l__xsim_tmpa_tl }
+ \bool_if:nT
+ {
+ \tl_if_eq_p:NN
+ \l__xsim_print_random_choice_tl
+ \c__xsim_print_random_both_tl
+ ||
+ \tl_if_eq_p:NN
+ \l__xsim_print_random_choice_tl
+ \c__xsim_print_random_exercise_tl
+ }
+ {
+ \xsim_if_exercise_exists:VVT
+ \l__xsim_tmpb_tl
+ \l__xsim_tmpa_tl
+ {
+ \xsim_insert:VVnn
+ \l__xsim_tmpb_tl
+ \l__xsim_tmpa_tl
+ {}
+ {exercise}
+ }
+ }
+ \bool_if:nT
+ {
+ \tl_if_eq_p:NN
+ \l__xsim_print_random_choice_tl
+ \c__xsim_print_random_both_tl
+ ||
+ \tl_if_eq_p:NN
+ \l__xsim_print_random_choice_tl
+ \c__xsim_print_random_solution_tl
+ }
+ {
+ \xsim_if_solution_exists:VVT
+ \l__xsim_tmpb_tl
+ \l__xsim_tmpa_tl
+ {
+ \xsim_insert:VVnn
+ \l__xsim_tmpb_tl
+ \l__xsim_tmpa_tl
+ {}
+ {solution}
+ }
+ }
+ }
+ }
+ }
+
+\keys_define:nn {xsim/random}
+ {
+ sort .bool_set:N = \l__xsim_sort_random_bool ,
+ sort .initial:n = true ,
+ collection .tl_set:N = \l_xsim_random_collection_tl ,
+ collection .initial:n = all~exercises ,
+ exclude .code:n = \__xsim_exclude_ids:n {#1} ,
+ print .choice: ,
+ print/exercises .code:n =
+ \tl_set:Nn \l__xsim_print_random_choice_tl {exercise} ,
+ print/solutions .code:n =
+ \tl_set:Nn \l__xsim_print_random_choice_tl {solution} ,
+ print/both .code:n =
+ \tl_set:Nn \l__xsim_print_random_choice_tl {both} ,
+ print/unknown .code:n =
+ \msg_warning:nnn {xsim} {print-collection-choice} {#1}
+ \tl_set:Nn \l__xsim_print_random_choice_tl {exercise} ,
+ print .initial:n = exercises
+ }
+
+\cs_new_protected:Npn \__xsim_exclude_ids:n #1
+ {
+ \seq_clear:N \l__xsim_tmpa_seq
+ \clist_map_inline:nn {#1}
+ {
+ \tl_set:Nx \l__xsim_tmpa_tl
+ { \xsim_get_id_for_property:nn {ID} {##1} }
+ \tl_if_blank:NTF \l__xsim_tmpa_tl
+ { \seq_put_right:Nn \l__xsim_tmpa_seq {##1} }
+ {
+ \seq_put_right:Nx \l__xsim_tmpa_seq
+ { \xsim_get_id_for_property:nn {ID} {##1} }
+ }
+ }
+ \seq_remove_duplicates:N \l__xsim_tmpa_seq
+ \tl_set:Nx \l_xsim_random_exclude_list_tl
+ { \seq_use:Nn \l__xsim_tmpa_seq {,} }
+ }
+
+% use this in the exclude list:
+% \xsim_get_type_for_property:nn {id} {}
+% or a combination of:
+% \xsim_get_type_for_property:nn {ID} {}
+% \xsim_get_id_for_property:nn {ID} {}
+
+% ----------------------------------------------------------------------------
+\file_input_stop:
diff --git a/macros/latex/contrib/xsim/code/xsim.solutions.code.tex b/macros/latex/contrib/xsim/code/xsim.solutions.code.tex
new file mode 100644
index 0000000000..b8ac06aca0
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsim.solutions.code.tex
@@ -0,0 +1,407 @@
+% ----------------------------------------------------------------------------
+% the XSIM package - solutions module
+%
+% eXercise Sheets IMproved
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+\XSIMmodule{solutions}{managing solutions}
+
+\xsim_load_modules:n {tags,properties}
+
+\bool_new:N \l_xsim_inside_solution_bool
+\bool_new:N \l__xsim_include_question_bool
+\bool_new:N \l__xsim_printsolutions_headings_bool
+\bool_new:N \l__xsim_printsolutions_conditions_bool
+\bool_new:N \l____xsim_this_condition_bool
+
+\prop_new:N \l__xsim_printsolutions_conditions_prop
+
+\int_new:N \l_xsim_printsolutions_section_int
+\int_new:N \l_xsim_printsolutions_chapter_int
+
+\tl_new:N \l__xsim_printsolutions_headings_template_tl
+\tl_new:N \ExerciseSection
+\tl_new:N \ExerciseChapter
+\tl_new:N \l__xsim_printsolutions_collection_tl
+
+% ----------------------------------------------------------------------------
+
+\prg_new_conditional:Npnn \xsim_if_inside_solution: {p,T,F,TF}
+ {
+ \bool_if:NTF \l_xsim_inside_solution_bool
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+\prg_new_conditional:Npnn \xsim_if_solution_print:n #1 {p,T,F,TF}
+ {
+ \bool_if:cTF {l__xsim_#1_solution_print_bool}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+% #1: type
+% #2: id
+\prg_new_protected_conditional:Npnn \xsim_solution_if_print:nn #1#2 {T,F,TF}
+ {
+ \bool_set_true:N \l____xsim_print_bool
+ \xsim_if_tagged:nnF {#1} {#2}
+ { \bool_set_false:N \l____xsim_print_bool }
+ \xsim_if_boolean_property:nnnF {#1} {#2} {print}
+ { \bool_set_false:N \l____xsim_print_bool }
+ \bool_if:cF {l__xsim_#1_solution_print_bool}
+ { \bool_set_false:N \l____xsim_print_bool }
+ \bool_if:NTF \l____xsim_print_bool
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\cs_generate_variant:Nn \xsim_solution_if_print:nnT {nV,oo}
+
+% #1: type
+% #2: id
+\prg_new_protected_conditional:Npnn \xsim_if_solution_exists:nn #1#2 {T,F,TF}
+ {
+ \__xsim_set_file_signature:nnn {#1} {#2} {solution}
+ \file_if_exist:VTF \l__xsim_file_path_and_name_tl
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+\cs_generate_variant:Nn \xsim_if_solution_exists:nnT {VV,oo}
+\cs_generate_variant:Nn \xsim_if_solution_exists:nnTF {oo}
+
+% #1: type
+% #2: options
+\cs_new_protected:Npn \xsim_start_solution:nn #1#2
+ {
+ \keys_set:xn { xsim/\xsim_get_parameter:nn {#1} {solution-env} } {#2}
+ \bool_set_true:N \l_xsim_inside_solution_bool
+ \refstepcounter { \xsim_get_parameter:nn {#1} {solution-counter} }
+ }
+
+\cs_new_protected:Npn \xsim_stop_solution:n #1 {}
+
+% ----------------------------------------------------------------------------
+
+% #1: conditional base function (three n args)% #2: name
+% #3: code (refer to type and id with `#1' and `#2'), #3 refers to the boolean
+% properties use/print
+\cs_new_protected:Npn \xsim_new_solutions_condition:Nnn #1#2#3
+ {
+ \prg_new_protected_conditional:Npnn #1 ##1##2##3 {T,F,TF}
+ {
+ \xsim_if_in_list:nnTF {##3} {##1-##2=={true}}
+ {
+ \__xsim_printsolutions_if_condition:nTF {#2}
+ { \__xsim_printsolutions_condition_true: #3 }
+ { \prg_return_false: }
+ }
+ { \prg_return_false: }
+ }
+ }
+
+% per section condition:
+\xsim_new_solutions_condition:Nnn \__xsim_if_solutions_per_section:nnn
+ {per-section}
+ {
+ \xsim_if_in_list:nxTF {section-value}
+ { #1-#2 == { \int_use:N \l_xsim_printsolutions_section_int } }
+ {
+ \tl_set:Nx \ExerciseSection
+ { \xsim_get_property:nnn {#1} {#2} {section} }
+ \prg_return_true:
+ }
+ { \prg_return_false: }
+ }
+
+% per chapter condition:
+\xsim_new_solutions_condition:Nnn \__xsim_if_solutions_per_chapter:nnn
+ {per-chapter}
+ {
+ \xsim_if_in_list:nxTF {chapter-value}
+ { #1-#2 == { \int_use:N \l_xsim_printsolutions_chapter_int } }
+ {
+ \tl_set:Nx \ExerciseChapter
+ { \xsim_get_property:nnn {#1} {#2} {chapter} }
+ \prg_return_true:
+ }
+ { \prg_return_false: }
+ }
+
+% per collection condition:
+\xsim_new_solutions_condition:Nnn \__xsim_if_solutions_in_collection:nnn
+ {per-collection}
+ {
+ \tl_if_blank:VTF \l__xsim_printsolutions_collection_tl
+ { \prg_return_false: }
+ {
+ \xsim_if_in_collection:VnnTF
+ \l__xsim_printsolutions_collection_tl
+ {#1}
+ {#2}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+ }
+
+% ----------------------------------------------------------------------------
+% conditions mechanism
+\cs_new_protected:Npn \__xsim_printsolutions_condition_false:
+ { \bool_set_false:N \l__xsim_printsolutions_conditions_bool }
+
+\cs_new_protected:Npn \__xsim_printsolutions_condition_true:
+ { \bool_set_true:N \l__xsim_printsolutions_conditions_bool }
+
+% #1: type
+% #2: id
+% #3: boolean property
+\prg_new_conditional:Npnn
+ \__xsim_printsolutions_if_conditions:nnn #1#2#3 {T,F,TF}
+ {
+ \bool_if:NTF \l__xsim_printsolutions_conditions_bool
+ { \prg_return_true: }
+ {
+ \xsim_if_in_list:nnTF {#3} {#1-#2=={true}}
+ { \prg_return_false: }
+ { \prg_return_true: }
+ }
+ }
+
+% #1: name
+% #2: condition
+\cs_new_protected:Npn \xsim_declare_printsolutions_condition:nn #1#2
+ {
+ \bool_new:c {l__xsim_printsolutions_#1_condition_bool}
+ \prop_put:Nnn \l__xsim_printsolutions_conditions_prop {#1} {#2}
+ }
+
+\prg_new_conditional:Npnn \__xsim_printsolutions_if_condition:n #1 {T,F,TF}
+ {
+ \bool_if:cTF {l__xsim_printsolutions_#1_condition_bool}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+% #1: name
+% #2: true|false
+\cs_new_protected:Npn \__xsim_printsolutions_condition:nn #1#2
+ { \bool_set_eq:cc {l__xsim_printsolutions_#1_condition_bool} {c_#2_bool} }
+
+% false if any condition is false for this solution:
+% #1: type
+% #2: id
+% #3: boolean property
+\prg_new_protected_conditional:Npnn
+ \__xsim_printsolutions_check_conditions:nnn #1#2#3 {T,F,TF}
+ {
+ \__xsim_printsolutions_condition_false:
+ \bool_set_true:N \l____xsim_this_condition_bool
+ \prop_map_inline:Nn \l__xsim_printsolutions_conditions_prop
+ {
+ \bool_if:cT {l__xsim_printsolutions_##1_condition_bool}
+ {
+ ##2 {#1} {#2} {#3}
+ { }
+ { \bool_set_false:N \l____xsim_this_condition_bool }
+ }
+ }
+ \bool_if:NTF \l____xsim_this_condition_bool
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+
+% #1: type
+% #2: id
+% #3: boolean property
+% #4: code
+\cs_new_protected:Npn \xsim_if_this_solution_do:nnnn #1#2#3#4
+ {
+ \xsim_if_solution_exists:nnT {#1} {#2}
+ { \__xsim_printsolutions_check_conditions:nnnT {#1} {#2} {#3} {#4} }
+ }
+
+% #1: type
+% #2: boolean property
+% #3: code
+\cs_new_protected:Npn \xsim_if_solutions_do:nnn #1#2#3
+ {
+ \xsim_foreach_exercise_type_id:nn {#2}
+ { \xsim_if_this_solution_do:nnnn {#1} {##2} {#2} {#3} }
+ }
+
+% ----------------------------------------------------------------------------
+% #1: boolean -- if true only the solutions of printed exercises will be
+% output
+% #2: pre
+% #3: post
+% #4: type
+% #5: code (refer to type with #1 and id with #2)
+\cs_new_protected:Npn \xsim_print_type_code:nnnnn #1#2#3#4#5
+ {
+ \group_begin:
+ \cs_set_protected:Npn \__xsim_print_entry:nn ##1##2 {#5}
+ #2
+ \bool_set_false:N \l__xsim_tmpa_bool
+ \bool_if:nTF {#1}
+ {
+ \xsim_if_solutions_do:nnn {#4} {print}
+ { \bool_set_true:N \l__xsim_tmpa_bool }
+ }
+ {
+ \xsim_if_solutions_do:nnn {#4} {use}
+ { \bool_set_true:N \l__xsim_tmpa_bool }
+ }
+ \tl_set:Nn \ExerciseType {#4}
+ \bool_if:NT \l__xsim_tmpa_bool
+ {
+ \bool_if:NT \l__xsim_printsolutions_headings_bool
+ {
+ \xsim_use_template:nV
+ {heading}
+ \l__xsim_printsolutions_headings_template_tl
+ }
+ }
+ \bool_if:nTF {#1}
+ {
+ \xsim_if_solutions_do:nnn {#4} {print}
+ { \__xsim_print_entry:nn {#4} {##2} }
+ }
+ {
+ \xsim_if_solutions_do:nnn {#4} {use}
+ { \__xsim_print_entry:nn {#4} {##2} }
+ }
+ #3
+ \group_end:
+ }
+
+% #1: boolean -- if true only the solutions of printed exercises will be
+% output
+% #2: options
+% #3: type
+\cs_new_protected:Npn \xsim_print_type_solutions:nnn #1#2#3
+ {
+ \xsim_print_type_code:nnnnn
+ {#1}
+ {
+ \xsim_verbose:n
+ { Printing~ solutions~ for~ exercise~ type~ `#3'. }
+ \keys_set:nn {xsim/print-solutions} {#2}
+ }
+ {}
+ {#3}
+ { \xsim_insert:nnnn {##1} {##2} {} {solution} }
+ }
+
+% #1: boolean -- if true only the solutions of printed exercises will be
+% output
+% #2: options
+\cs_new_protected:Npn \xsim_print_all_solutions_per_type:nn #1#2
+ {
+ \xsim_foreach_exercise_type:n
+ { \xsim_print_type_solutions:nnn {#1} {#2} {##1} }
+ }
+
+% #1: boolean -- if true only the solutions of printed exercises will be
+% output
+% #2: options
+\cs_new_protected:Npn \xsim_print_all_solutions_per_id:nn #1#2
+ {
+ \xsim_verbose:n { Printing~ solutions. }
+ \group_begin:
+ \keys_set:nn {xsim/print-solutions} {#2}
+ \bool_set_false:N \l__xsim_tmpa_bool
+ \bool_if:nTF {#1}
+ {
+ \xsim_foreach_exercise_id_type:nn {print}
+ {
+ \xsim_if_this_solution_do:nnnn {##1} {##2} {print}
+ { \xsim_insert:nnnn {##1} {##2} {} {solution} }
+ }
+ }
+ {
+ \xsim_foreach_exercise_id_type:nn {use}
+ {
+ \xsim_if_this_solution_do:nnnn {##1} {##2} {use}
+ { \xsim_insert:nnnn {##1} {##2} {} {solution} }
+ }
+ }
+ \group_end:
+ }
+
+% ----------------------------------------------------------------------------
+
+\xsim_declare_printsolutions_condition:nn {per-section}
+ { \__xsim_if_solutions_per_section:nnnTF }
+\xsim_declare_printsolutions_condition:nn {per-chapter}
+ { \__xsim_if_solutions_per_chapter:nnnTF }
+\xsim_declare_printsolutions_condition:nn {per-collection}
+ { \__xsim_if_solutions_in_collection:nnnTF }
+
+% ----------------------------------------------------------------------------
+
+\keys_define:nn {xsim/print-solutions}
+ {
+ headings .bool_set:N = \l__xsim_printsolutions_headings_bool ,
+ headings .initial:n = true ,
+ headings-template .tl_set:N = \l__xsim_printsolutions_headings_template_tl ,
+ headings-template .initial:n = default ,
+ include-exercise .bool_set:N = \l__xsim_include_question_bool ,
+ include-exercise .initial:n = false ,
+ section .choice: ,
+ section/true .code:n =
+ \__xsim_printsolutions_condition:nn {per-section} {true}
+ \int_set:Nn \l_xsim_printsolutions_section_int { \value {section} } ,
+ section/false .code:n =
+ \__xsim_printsolutions_condition:nn {per-section} {false} ,
+ section/unknown .code:n =
+ \__xsim_printsolutions_condition:nn {per-section} {true}
+ \int_set:Nn \l_xsim_printsolutions_section_int {#1} ,
+ section .default:n = true ,
+ section .initial:n = false ,
+ chapter .choice: ,
+ chapter/true .code:n =
+ \__xsim_printsolutions_condition:nn {per-chapter} {true}
+ \int_set:Nn \l_xsim_printsolutions_chapter_int { \value {chapter} } ,
+ chapter/false .code:n =
+ \__xsim_printsolutions_condition:nn {per-chapter} {false} ,
+ chapter/unknown .code:n =
+ \__xsim_printsolutions_condition:nn {per-chapter} {true}
+ \int_set:Nn \l_xsim_printsolutions_chapter_int {#1} ,
+ chapter .default:n = true ,
+ chapter .initial:n = false ,
+ collection .choice: ,
+ collection/false .code:n =
+ \__xsim_printsolutions_condition:nn {per-collection} {false} ,
+ collection/unknown .code:n =
+ \xsim_if_collection_exists:nT {#1}
+ {
+ \__xsim_printsolutions_condition:nn {per-collection} {true}
+ \tl_set:Nn \l__xsim_printsolutions_collection_tl {#1}
+ } ,
+ collection .initial:n = false
+ }
+
+% ----------------------------------------------------------------------------
+
+\file_input_stop:
diff --git a/macros/latex/contrib/xsim/code/xsim.sty b/macros/latex/contrib/xsim/code/xsim.sty
new file mode 100644
index 0000000000..9374722397
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsim.sty
@@ -0,0 +1,109 @@
+% ----------------------------------------------------------------------------
+% the XSIM package
+%
+% eXercise Sheets IMproved
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+\RequirePackage {expl3,xparse}
+
+\ExplSyntaxOn
+
+\tl_const:Nn \c_xsim_date_tl {2018/02/12}
+\tl_const:Nn \c_xsim_version_major_number_tl {0}
+\tl_const:Nn \c_xsim_version_minor_number_tl {11}
+\tl_const:Nn \c_xsim_version_subrelease_tl {}
+\tl_const:Nx \c_xsim_version_number_tl
+ {
+ \c_xsim_version_major_number_tl .
+ \c_xsim_version_minor_number_tl
+ }
+\tl_const:Nx \c_xsim_version_tl
+ {
+ \c_xsim_version_number_tl
+ \c_xsim_version_subrelease_tl
+ }
+\tl_const:Nn \c_xsim_info_tl {eXercise ~ Sheets ~ IMproved}
+
+\ProvidesExplPackage
+ {xsim}
+ {\c_xsim_date_tl}
+ {\c_xsim_version_tl}
+ {\c_xsim_info_tl}
+
+% --------------------------------------------------------------------------
+
+% options, information
+
+\bool_new:N \g_xsim_final_bool
+\bool_new:N \g_xsim_verbose_bool
+\bool_new:N \g_xsim_clear_aux_bool
+
+\keys_define:nn {xsim}
+ {
+ final .bool_gset:N = \g_xsim_final_bool ,
+ verbose .bool_gset:N = \g_xsim_verbose_bool ,
+ clear-aux .bool_gset:N = \g_xsim_clear_aux_bool
+ }
+
+\DeclareOption {final}
+ { \bool_gset_true:N \g_xsim_final_bool }
+
+\DeclareOption {verbose}
+ { \bool_gset_true:N \g_xsim_verbose_bool }
+
+\DeclareOption {clear-aux}
+ { \bool_gset_true:N \g_xsim_clear_aux_bool }
+
+\ProcessOptions \scan_stop:
+
+% --------------------------------------------------------------------------
+
+% messages:
+\msg_new:nnn {xsim} {rerun}
+ {
+ Exercise~ properties~ may~ have~ changed.~ Rerun~ to~ get~ them~
+ synchronized.
+ }
+
+\msg_new:nnn {xsim} {verbose} { #1 ~(\msg_line_context:) }
+
+% --------------------------------------------------------------------------
+
+\bool_new:N \g__xsim_rerun_bool
+
+\cs_new_protected:Npn \xsim_do_rerun:
+ { \xsim_if_rerun:T { \msg_warning:nn {xsim} {rerun} } }
+
+% --------------------------------------------------------------------------
+
+% load module mechanism:
+\tl_set:Nn \l_tmpa_tl {code.tex}
+\@onefilewithoptions {xsim.modules} [][] \l_tmpa_tl
+
+% --------------------------------------------------------------------------
+\xsim_load_modules:n {definitions}
+
+\AtEndDocument { \xsim_do_rerun: }
+
+\file_input_stop:
diff --git a/macros/latex/contrib/xsim/code/xsim.style.layouts.code.tex b/macros/latex/contrib/xsim/code/xsim.style.layouts.code.tex
new file mode 100644
index 0000000000..e8a43b0992
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsim.style.layouts.code.tex
@@ -0,0 +1,77 @@
+% ----------------------------------------------------------------------------
+% the XSIM package - layouts style
+%
+% eXercise Sheets IMproved
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+\xsimstyle{layouts}[2017/09/14]
+
+\RequirePackage{needspace}
+
+\DeclareExerciseEnvironmentTemplate{runin}
+ {%
+ \par\vspace{\baselineskip}
+ \Needspace*{2\baselineskip}
+ \noindent
+ \textbf{\XSIMmixedcase{\GetExerciseName}~\GetExerciseProperty{counter}}%
+ \GetExercisePropertyT{subtitle}{ \textit{#1}} % <<< notice the space
+ \IfInsideSolutionF{%
+ \GetExercisePropertyT{points}{%
+ \marginpar{%
+ \printgoal{\PropertyValue}%
+ \GetExercisePropertyT{bonus-points}{+\printgoal{\PropertyValue}}%
+ \,\IfExerciseGoalSingularTF{points}
+ {\XSIMtranslate{point}}
+ {\XSIMtranslate{points}}%
+ }%
+ }%
+ }%
+ }
+ {}
+
+\RequirePackage{array}
+
+\DeclareExerciseEnvironmentTemplate{margin}
+ {%
+ \trivlist
+ \item[\llap{%
+ \smash{%
+ \tabular[t]{@{}r@{}}
+ \textbf{\XSIMmixedcase{\GetExerciseName}~\GetExerciseProperty{counter}}
+ \IfExercisePropertySetT{points}{%
+ \tabularnewline
+ (%
+ \printgoal{\GetExerciseProperty{points}}%
+ \GetExercisePropertyT{bonus-points}{+\printgoal{#1}}%
+ \,\XSIMtranslate{point-abbr}%
+ )%
+ }%
+ \endtabular
+ }%
+ }]\relax
+ }
+ {\endtrivlist}
+
+% ----------------------------------------------------------------------------
+\endinput
diff --git a/macros/latex/contrib/xsim/code/xsim.tags.code.tex b/macros/latex/contrib/xsim/code/xsim.tags.code.tex
new file mode 100644
index 0000000000..4fac1762f8
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsim.tags.code.tex
@@ -0,0 +1,211 @@
+% ----------------------------------------------------------------------------
+% the XSIM package - tags module
+%
+% eXercise Sheets IMproved
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+\XSIMmodule{tags}{tagging of exercises}
+
+\xsim_load_modules:n {properties}
+
+\seq_new:N \l__xsim_tag_types_seq
+\bool_new:N \l____xsim_tagged_bool
+
+\cs_new_protected:Npn \xsim_set_seq_from_clist:Nn #1#2
+ {
+ \seq_clear:N #1
+ \clist_map_inline:nn {#2}
+ { \tl_if_blank:nF {##1} { \seq_put_right:Nn #1 {##1} } }
+ }
+\cs_generate_variant:Nn \xsim_set_seq_from_clist:Nn {c,Nx}
+
+\msg_new:nnn {xsim} {tag-exists}
+ { The~ tag~ type~ `#1'~ already~ exists.~ I~ am~ doing~ nothing. }
+
+% #1: name
+\cs_new_protected:Npn \xsim_new_tag_type:n #1
+ {
+ \xsim_verbose:n { Declaring~ new~ tag~ type~ `#1'. }
+ \seq_put_right:Nn \l__xsim_tag_types_seq {#1}
+ \seq_remove_duplicates:N \l__xsim_tag_types_seq
+ \seq_new:c {l__xsim_chosen_tags_#1_seq}
+ \bool_new:c {l__xsim_ignore_untagged_#1_bool}
+ \bool_new:c {l____xsim_tagged_#1_bool}
+ \keys_define:nn {xsim}
+ {
+ #1/ingore-untagged .bool_set:c = {l__xsim_ignore_untagged_#1_bool} ,
+ #1/ignore-untagged .initial:n = true ,
+ #1 .code:n =
+ \xsim_set_seq_from_clist:cn {l__xsim_chosen_tags_#1_seq} {##1}
+ }
+ \prg_new_protected_conditional:cpnn {xsim_if_#1_value:n} ##1 {T,F,TF}
+ {
+ \seq_if_in:cnTF {l__xsim_chosen_tags_#1_seq} {##1}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+ \xsim_declare_property:nnnn
+ { \c_false_bool }
+ { \c_false_bool }
+ { \c_false_bool }
+ {#1}
+ \xsim_declare_printsolutions_condition:nn {#1}
+ { \__xsim_printsolutions_if_tagged:nnnnTF {#1} }
+ \keys_define:nn {xsim/print-solutions}
+ {
+ #1 .choice: ,
+ #1/false .code:n =
+ \__xsim_printsolutions_condition:nn {#1} {false} ,
+ #1/true .code:n =
+ \__xsim_printsolutions_condition:nn {#1} {true} ,
+ #1/unknown .code:n =
+ \keys_set:nn {xsim} { #1 = ##1 }
+ \__xsim_printsolutions_condition:nn {#1} {true} ,
+ #1 .initial:n = false
+ }
+ }
+
+% #1: tag type
+\prg_new_protected_conditional:Npnn \xsim_if_tag_type_exists:n #1 {T,F,TF}
+ {
+ \seq_if_in:NnTF \l__xsim_tag_types_seq {#1}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+% #1: tag type
+% #2: exercise type
+% #3: id
+% #4: print|use
+\prg_new_protected_conditional:Npnn
+ \__xsim_printsolutions_if_tagged:nnnn #1#2#3#4 {T,F,TF}
+ {
+ \xsim_if_in_list:nnTF {#4} {#2-#3=={true}}
+ {
+ \__xsim_printsolutions_if_condition:nTF {#1}
+ {
+ \__xsim_printsolutions_condition_true:
+ \xsim_if_tagged:nnnTF {#2} {#3} {#1}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+ { \prg_return_false: }
+ }
+ { \prg_return_false: }
+ }
+
+% #1: exercise type
+% #2: id
+% #3: tag type
+\prg_new_protected_conditional:Npnn \xsim_if_tagged:nnn #1#2#3 {T,F,TF}
+ {
+ \bool_set_false:c {l____xsim_tagged_#3_bool}
+ \seq_if_empty:cTF {l__xsim_chosen_tags_#3_seq}
+ { \bool_set_true:c {l____xsim_tagged_#3_bool} }
+ {
+ \xsim_set_seq_from_clist:Nx
+ \l__xsim_tmpa_seq
+ { \xsim_get_property:nnn {#1} {#2} {#3} }
+ \seq_if_empty:NTF \l__xsim_tmpa_seq
+ {
+ \bool_set_eq:cc
+ {l____xsim_tagged_#3_bool}
+ {l__xsim_ignore_untagged_#3_bool}
+ }
+ {
+ \seq_map_inline:Nn \l__xsim_tmpa_seq
+ {
+ \seq_if_in:cnT {l__xsim_chosen_tags_#3_seq} {##1}
+ {
+ \bool_set_true:c {l____xsim_tagged_#3_bool}
+ \seq_map_break:
+ }
+ }
+ }
+ }
+ \bool_if:cTF {l____xsim_tagged_#3_bool}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+% #1: exercise type
+% #2: id
+\prg_new_protected_conditional:Npnn \xsim_if_tagged:nn #1#2 {T,F,TF}
+ {
+ \bool_set_true:N \l____xsim_tagged_bool
+ \seq_map_inline:Nn \l__xsim_tag_types_seq
+ {
+ \xsim_if_tagged:nnnF {#1} {#2} {##1}
+ {
+ \bool_set_false:N \l____xsim_tagged_bool
+ \seq_map_break:
+ }
+ }
+ \bool_if:NTF \l____xsim_tagged_bool
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+
+% #1: exercise type
+% #2: id
+% #3: tag type
+% #4: code
+\cs_new_protected:Npn \xsim_foreach_exercise_tag:nnnn #1#2#3#4
+ {
+ \xsim_set_seq_from_clist:Nx \l__xsim_tmpa_seq
+ { \xsim_get_property:nnn {#1} {#2} {#3} }
+ \seq_map_inline:Nn \l__xsim_tmpa_seq {#4}
+ }
+\cs_generate_variant:Nn \xsim_foreach_exercise_tag:nnnn {oo}
+
+% #1: exercise type
+% #2: id
+% #3: tag type
+% #4: sep
+\cs_new_protected:Npn \xsim_exercise_tags_use:nnnn #1#2#3#4
+ {
+ \xsim_set_seq_from_clist:Nx \l__xsim_tmpa_seq
+ { \xsim_get_property:nnn {#1} {#2} {#3} }
+ \seq_use:Nn \l__xsim_tmpa_seq {#4}
+ }
+\cs_generate_variant:Nn \xsim_exercise_tags_use:nnnn {oo}
+
+% #1: exercise type
+% #2: id
+% #3: tag type
+% #4: sep between two
+% #5: sep between more than two
+% #6: sep between last two
+\cs_new_protected:Npn \xsim_exercise_tags_use:nnnnnn #1#2#3#4#5#6
+ {
+ \xsim_set_seq_from_clist:Nx \l__xsim_tmpa_seq
+ { \xsim_get_property:nnn {#1} {#2} {#3} }
+ \seq_use:Nnnn \l__xsim_tmpa_seq {#4} {#5} {#6}
+ }
+\cs_generate_variant:Nn \xsim_exercise_tags_use:nnnn {oo}
+\cs_generate_variant:Nn \xsim_exercise_tags_use:nnnnnn {oo}
+
+% ----------------------------------------------------------------------------
+\file_input_stop:
diff --git a/macros/latex/contrib/xsim/code/xsim.templates.code.tex b/macros/latex/contrib/xsim/code/xsim.templates.code.tex
new file mode 100644
index 0000000000..9ed767f4a7
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsim.templates.code.tex
@@ -0,0 +1,172 @@
+% ----------------------------------------------------------------------------
+% the XSIM package - templates module
+%
+% eXercise Sheets IMproved
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+\XSIMmodule{templates}{properties of exercises}
+
+\xsim_load_modules:n {base}
+
+\msg_new:nnn {xsim} {unknown-template}
+ {
+ You~ are~ trying~ to~ load~ the~ template~ `#2'~ (template~ type~ `#1')~
+ \msg_line_context: .~ This~ template~ does~ not~ seem~ to~ be~ defined.~
+ I~ am~ using~ the~ template~ `#3'~ (template~ type~ `#1')~ instead.
+ }
+
+% #1: template type
+\cs_new_protected:Npn \xsim_new_template_type:n #1
+ {
+ \xsim_verbose:n { Declaring~ new~ template~ type~ `#1'. }
+ \prop_new:c {l__xsim_#1_template_prop}
+ }
+
+% #1: template type
+% #2: template name
+% #3: code
+\cs_new_protected:Npn \__xsim_add_template:nnn #1#2#3
+ {
+ \xsim_verbose:n { Defining~ new~ template~ `#2'~ of~ type~ `#1'. }
+ \tl_new:c {l__xsim_template_#1_#2_setup_tl}
+ \prop_put:cnn {l__xsim_#1_template_prop} {#2} {#3}
+ }
+
+\prg_new_conditional:Npnn \xsim_if_template_exists:nn #1#2 {T,F,TF}
+ {
+ \prop_if_in:cnTF {l__xsim_#1_template_prop} {#2}
+ { \prg_return_true: }
+ { \prg_return_false: }
+ }
+
+% #1: template type
+% #2: template name
+\cs_new_protected:Npn \__xsim_get_template:nn #1#2
+ { \prop_item:cn {l__xsim_#1_template_prop} {#2} }
+
+% ----------------------------------------------------------------------------
+
+% environment templates (types `begin' and `end'):
+\xsim_new_template_type:n {begin}
+\xsim_new_template_type:n {end}
+
+% #1: template name
+% #2: begin code
+% #3: end code
+\cs_new_protected:Npn \xsim_declare_environment_template:nnn #1#2#3
+ {
+ \__xsim_add_template:nnn {begin} {#1}
+ {
+ \__xsim_start_template:nn {begin} {#1}
+ \__xsim_start_template:nn {end} {#1}
+ #2
+ }
+ \__xsim_add_template:nnn {end} {#1}
+ {
+ #3
+ \__xsim_stop_template:nn {end} {#1}
+ \__xsim_stop_template:nn {begin} {#1}
+ }
+ }
+
+% ----------------------------------------------------------------------------
+
+% heading templates for the solution list (type `heading'):
+\xsim_new_template_type:n {heading}
+
+% #1: template name
+% #2: code
+\cs_new_protected:Npn \xsim_declare_heading_template:nn #1#2
+ {
+ \__xsim_add_template:nnn {heading} {#1}
+ {
+ \__xsim_start_template:nn {heading} {#1}
+ #2
+ \__xsim_stop_template:nn {heading} {#1}
+ }
+ }
+
+% ----------------------------------------------------------------------------
+
+% grading table templates (type `table'):
+\xsim_new_template_type:n {table}
+
+% #1: template name
+% #2: code
+\cs_new_protected:Npn \xsim_declare_table_template:nn #1#2
+ {
+ \__xsim_add_template:nnn {table} {#1}
+ {
+ \__xsim_start_template:nn {table} {#1}
+ #2
+ \__xsim_stop_template:nn {table} {#1}
+ }
+ }
+
+% ----------------------------------------------------------------------------
+
+% using templates:
+% setup up the next usage of `template name':
+% #1: template type
+% #2: template name
+% #3: setup code
+\cs_new_protected:Npn \xsim_setup_template:nnn #1#2#3
+ { \tl_set:cn {l__xsim_template_#1_#2_setup_tl} {#3} }
+\cs_generate_variant:Nn \xsim_setup_template:nnn {nnV,nVn}
+
+% #1: template type
+% #2: template name
+\cs_new_protected:Npn \__xsim_setup_template:nn #1#2
+ { \tl_use:c {l__xsim_template_#1_#2_setup_tl} }
+
+% #1: template type
+% #2: template name
+\cs_new_protected:Npn \__xsim_clear_template_setup:nn #1#2
+ { \tl_clear:c {l__xsim_template_#1_#2_setup_tl} }
+
+% #1: template type
+% #2: template name
+\cs_new_protected:Npn \__xsim_start_template:nn #1#2
+ { \group_begin: \__xsim_setup_template:nn {#1} {#2} }
+
+% #1: template type
+% #2: template name
+\cs_new_protected:Npn \__xsim_stop_template:nn #1#2
+ { \group_end: \__xsim_clear_template_setup:nn {#1} {#2} }
+
+% #1: template type
+% #2: template name
+\cs_new_protected:Npn \xsim_use_template:nn #1#2
+ {
+ \xsim_if_template_exists:nnTF {#1} {#2}
+ { \__xsim_get_template:nn {#1} {#2} }
+ {
+ \msg_warning:nnnnn {xsim} {unknown-template} {#1} {#2} {default}
+ \__xsim_get_template:nn {#1} {default}
+ }
+ }
+\cs_generate_variant:Nn \xsim_use_template:nn {nx,nV}
+
+% ----------------------------------------------------------------------------
+\file_input_stop:
diff --git a/macros/latex/contrib/xsim/code/xsim.translations.code.tex b/macros/latex/contrib/xsim/code/xsim.translations.code.tex
new file mode 100644
index 0000000000..e398575c40
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsim.translations.code.tex
@@ -0,0 +1,146 @@
+% ----------------------------------------------------------------------------
+% the XSIM package - translations module
+%
+% eXercise Sheets IMproved
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+\XSIMmodule{translations}{language settings for XSIM}
+
+\RequirePackage{translations}
+
+\msg_new:nnn {xsim} {language-not-defined}
+ {
+ You~ chose~ the~ language~ `#1'~ which~ is~ not~ defined~ by~ xsim.~
+ `english'~ is~ used~ instead.~ If~ you~ just~ mistyped~ try~ again!~
+ Otherwise~ contact~ the~ author~ and~ he'll~ probably~ add~ your~ language.
+ }
+
+% --------------------------------------------------------------------------
+
+\bool_new:N \l__xsim_language_auto_bool
+\bool_set_true:N \l__xsim_language_auto_bool
+% this token list will hold the chosen language for xsim; since the
+% language is either chosen automatically or by option it is only available at
+% begin document
+\tl_new:N \l_xsim_language_tl
+\tl_set:Nn \l_xsim_language_tl {english}
+\tl_new:N \l__xsim_current_language_tl
+
+\tl_const:Nn \c__xsim_keyword_prefix_tl {xsim-keyword-}
+
+% ----------------------------------------------------------------------------
+
+\prop_new:N \g_xsim_translations_prop
+
+% translate the key #1
+\cs_new:Npn \xsim_translate:n #1
+ {
+ \bool_if:NTF \l__xsim_language_auto_bool
+ { \GetTranslation { \c__xsim_keyword_prefix_tl #1 } }
+ {
+ \GetTranslationFor
+ { \l_xsim_language_tl }
+ { \c__xsim_keyword_prefix_tl #1 }
+ }
+ }
+
+\AtBeginDocument{
+ \bool_if:NTF \l__xsim_language_auto_bool
+ {
+ \tl_set:Nx \l_xsim_language_tl
+ { \@trnslt@language{\@trnslt@current@language} }
+ }
+ {
+ \tl_set_eq:NN
+ \l_xsim_language_tl
+ \l__xsim_current_language_tl
+ }
+}
+
+% ----------------------------------------------------------------------------
+% #1: keyword
+% #2: language
+% #3: translation
+\cs_new_protected:Npn \__xsim_declare_translation:nw #1#2=#3\q_stop
+ {
+ \tl_set:Nx \l__xsim_tmpa_tl { \tl_trim_spaces:n {#2} }
+ \tl_set:Nx \l__xsim_tmpb_tl { \tl_trim_spaces:n {#3} }
+ \xsim_declare_translation:VnV
+ \l__xsim_tmpa_tl
+ {#1}
+ \l__xsim_tmpb_tl
+ }
+
+% #1: language
+% #2: keyword
+% #3: translation
+\cs_new_protected:Npn \xsim_declare_translation:nnn #1#2#3
+ {
+ \xsim_verbose:n
+ { Declaring~ `#1'~ tranlation~ of~ `#2':~ `#3'. }
+ \declaretranslation
+ {#1}
+ { \c__xsim_keyword_prefix_tl #2 }
+ {#3}
+ \prop_gput:Nnn \g_xsim_translations_prop {#2(#1)} {#3}
+ }
+\cs_generate_variant:Nn \xsim_declare_translation:nnn {V,VnV}
+
+% #1: key
+% #2: csv list: { <lang1> = <translation1> , <lang2> = <translation2> }
+\cs_new_protected:Npn \xsim_declare_translations:nn #1#2
+ {
+ \clist_map_inline:nn {#2}
+ { \__xsim_declare_translation:nw {#1} ##1 \q_stop }
+ }
+
+\cs_new:Npn \__xsim_parse_translate_list_entry:nnn #1#2#3 {}
+
+\cs_new_protected:Npn \__xsim_parse_translate_list_entry:www #1(#2)\q_mark#3\q_stop
+ { \__xsim_parse_translate_list_entry:nnn {#1} {#2} {#3} }
+
+\cs_new_protected:Npn \xsim_for_all_translations_do:n #1
+ {
+ \cs_set:Npn \__xsim_parse_translate_list_entry:nnn ##1##2##3 {#1}
+ \prop_map_inline:Nn \g_xsim_translations_prop
+ { \__xsim_parse_translate_list_entry:www ##1 \q_mark ##2 \q_stop }
+ }
+
+% ----------------------------------------------------------------------------
+
+\keys_define:nn {xsim}
+ {
+ language .value_required:n = true ,
+ language .code:n =
+ \tl_if_eq:nnTF {#1} {auto}
+ { \bool_set_true:N \l__xsim_language_auto_bool }
+ {
+ \bool_set_false:N \l__xsim_language_auto_bool
+ \tl_set:Nn \l__xsim_current_language_tl {#1}
+ } ,
+ language .initial:n = auto
+ }
+
+% ----------------------------------------------------------------------------
+\file_input_stop:
diff --git a/macros/latex/contrib/xsim/code/xsim.verbwrite.code.tex b/macros/latex/contrib/xsim/code/xsim.verbwrite.code.tex
new file mode 100644
index 0000000000..83124c5ff2
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsim.verbwrite.code.tex
@@ -0,0 +1,209 @@
+% ----------------------------------------------------------------------------
+% the XSIM package - verbwrite module
+%
+% eXercise Sheets IMproved
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+\XSIMmodule{verbwrite}{write contents of environments verbatim to files}
+
+\xsim_load_modules:n {base}
+
+\iow_new:N \l__file_contents_iow
+\tl_new:N \l_xsim_file_begin_tl
+\tl_new:N \l_xsim_file_end_tl
+\int_new:N \l_xsim_line_gobble_int
+\int_zero:N \l_xsim_line_gobble_int
+
+\tl_const:Nx \c__xsim_backslash_char_tl { \cs_to_str:N \\ }
+
+\group_begin:
+ \char_set_catcode_other:n {37}
+ \tl_const:Nn \c__xsim_percent_char_tl {%}
+\group_end:
+
+% ----------------------------------------------------------------------------
+
+% the `final' option will prevent writing to files:
+% #1: write stream
+% #2: file name
+\cs_new_protected:Npn \__xsim_open_stream:Nn #1#2
+ {
+ \file_if_exist:nTF {#2}
+ { \xsim_if_final:F { \iow_open:Nn #1 {#2} } }
+ {
+ % \g_xsim_final_bool is a global variable; however, if it is true and
+ % the exercise file doesn't exist, yet, we need to set it locally to
+ % false in order to ensure that the file is correctly written and the
+ % stream closed afterwards
+ \bool_set_false:N \g_xsim_final_bool
+ \iow_open:Nn #1 {#2}
+ }
+ }
+
+% #1: write stream
+\cs_new_protected:Npn \__xsim_close_stream:N #1
+ { \xsim_if_final:F { \iow_close:N #1 } }
+
+% #1: write stream
+% #1: contents
+\cs_new_protected:Npn \__xsim_write_to_stream:Nn #1#2
+ {
+ \xsim_if_final:F
+ {
+ % remove the /one/ space token with catcode 10 that is inserted if
+ % no options are given to the surrounding environment:
+ \tl_set:Nn \l__xsim_tmpa_tl {#2}
+ \tl_remove_once:Nn \l__xsim_tmpa_tl {~}
+ \int_zero:N \l__xsim_tmpa_int
+ \int_while_do:nn { \l__xsim_tmpa_int < \l_xsim_line_gobble_int }
+ {
+ \int_incr:N \l__xsim_tmpa_int
+ \tl_set:Nx \l__xsim_tmpa_tl { \tl_tail:N \l__xsim_tmpa_tl }
+ }
+ \iow_now:NV #1 \l__xsim_tmpa_tl
+ }
+ }
+\cs_generate_variant:Nn \__xsim_write_to_stream:Nn {Nx}
+
+\cs_new:Npn \__xsim_tab: { \c_space_tl \c_space_tl }
+\cs_new:Npn \__xsim_par: { ^^J ^^J }
+
+% the following is inspired by the definition of the `filecontents'
+% environment:
+% #1: boolean - if true an active eol needs to be inserted before
+% starting to write
+% #2: file name
+\cs_new_protected:Npn \xsim_file_write_start:nn #1#2
+ {
+ % we need to insert an active ^^M if no options are given
+ % see http://tex.stackexchange.com/q/9035/5049 reasons
+ \use:nx
+ { \__xsim_file_write_start:n {#2} }
+ { \bool_if:nF {#1} { \c__xsim_active_eol_tl } }
+ }
+\cs_generate_variant:Nn \xsim_file_write_start:nn {nV}
+
+\group_begin:
+\char_set_catcode_active:n {13} % ^^M (carriage return, endlinechar)
+\char_set_catcode_active:n {12} % ^^L (form feed)
+\char_set_catcode_active:n {9} % ^^I (horizontal tab)
+%
+\tl_const:Nn \c__xsim_active_eol_tl {^^M} %
+%
+% #1: file name
+\cs_new_protected:Npn \__xsim_file_write_start:n #1 %
+ { %
+ \group_begin: %
+ \xsim_if_final:TF %
+ { \xsim_verbose:x { Not~ (re-)writing~ file~ `#1'. } } %
+ { \xsim_verbose:x { (Re-)writing~ file~ `#1'. } } %
+ \__xsim_open_stream:Nn \l__file_contents_iow {#1} %
+ \tl_if_blank:VF \l_xsim_file_begin_tl %
+ { %
+ \xsim_if_final:F %
+ { %
+ \iow_now:Nx \l__file_contents_iow %
+ { \l_xsim_file_begin_tl } %
+ } %
+ } %
+ \seq_map_inline:Nn \l_char_special_seq %
+ { \char_set_catcode_other:N ##1 } %
+ \int_step_inline:nnnn {128} {1} {255} %
+ { \char_set_catcode_letter:n {##1} } %
+ \tl_set:Nx \l__xsim_tmpa_tl %
+ { \c__xsim_backslash_char_tl end \cs_to_str:N \{ \@currenvir \cs_to_str:N \} } %
+ \use:x %
+ { %
+ \cs_set:cpn {__xsim_tmpa:www} %
+ ####1 \l__xsim_tmpa_tl %
+ ####2 \l__xsim_tmpa_tl %
+ ####3 \exp_not:N \q_stop: %
+ } %
+ { %
+ \tl_if_blank:nTF {##3} %
+ { \__xsim_write_to_stream:Nn \l__file_contents_iow {##1} } %
+ { %
+ \cs_set:Npx \__xsim_M:w { \exp_not:N \end {\@currenvir} } %
+ \char_set_active_eq:nN {13} \__xsim_M:w %
+ % the last line is `##1 \end{\@currenvir} ##2':
+ % \tl_if_blank:nF {##1} %
+ % { \__xsim_write_to_stream:Nn \l__file_contents_iow {##1} }%
+ % \tl_if_blank:nF {##2} %
+ % {}%
+ } %
+ ^^M %
+ } %
+ \char_set_catcode_active:n {13} %
+ \char_set_catcode_active:n {12} %
+ \char_set_catcode_active:n {9} %
+ \cs_set:Npx \__xsim_M:w ##1 ^^M %
+ { %
+ \exp_not:N \__xsim_tmpa:www ##1 %
+ \l__xsim_tmpa_tl %
+ \l__xsim_tmpa_tl %
+ \exp_not:N \q_stop: %
+ } %
+ \char_set_active_eq:nN {13} \__xsim_M:w %
+ \char_set_active_eq:nN {12} \__xsim_par: %
+ \char_set_active_eq:nN {9} \__xsim_tab: %
+ } %
+\group_end:
+
+\cs_new_protected:Npn \xsim_file_write_stop:
+ {
+ \tl_if_blank:VF \l_xsim_file_end_tl
+ {
+ \xsim_if_final:F
+ {
+ \iow_now:Nx \l__file_contents_iow
+ { \l_xsim_file_end_tl }
+ }
+ }
+ \__xsim_close_stream:N \l__file_contents_iow
+ \group_end:
+ }
+
+% ----------------------------------------------------------------------------
+
+\NewDocumentCommand \XSIMfilewritestart {sm}
+ {
+ \IfBooleanTF {#1}
+ { \xsim_file_write_start:nn { \c_false_bool } {#2} }
+ { \xsim_file_write_start:nn { \c_true_bool } {#2} }
+ }
+
+\NewDocumentCommand \XSIMfilewritestop {}
+ { \xsim_file_write_stop: }
+
+\NewDocumentCommand \XSIMsetfilebegin {+m}
+ { \tl_set:Nn \l_xsim_file_begin_tl {#1} }
+
+\NewDocumentCommand \XSIMsetfileend {+m}
+ { \tl_set:Nn \l_xsim_file_end_tl {#1} }
+
+\NewDocumentCommand \XSIMgobblechars {m}
+ { \int_set:Nn \l_xsim_line_gobble_int {#1} }
+
+% ----------------------------------------------------------------------------
+\file_input_stop:
diff --git a/macros/latex/contrib/xsim/code/xsimverb.sty b/macros/latex/contrib/xsim/code/xsimverb.sty
new file mode 100644
index 0000000000..8064d0d06d
--- /dev/null
+++ b/macros/latex/contrib/xsim/code/xsimverb.sty
@@ -0,0 +1,93 @@
+% ----------------------------------------------------------------------------
+% the XSIMVERB package
+%
+% write environments verbatim to files
+%
+% ----------------------------------------------------------------------------
+% Clemens Niederberger
+% Web: https://github.com/cgnieder/xsim
+% E-Mail: contact@mychemistry.eu
+% ----------------------------------------------------------------------------
+% Copyright 2017--2018 Clemens Niederberger
+%
+% This work may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.3
+% of this license or (at your option) any later version.
+% The latest version of this license is in
+% http://www.latex-project.org/lppl.txt
+% and version 1.3 or later is part of all distributions of LaTeX
+% version 2005/12/01 or later.
+%
+% This work has the LPPL maintenance status `maintained'.
+%
+% The Current Maintainer of this work is Clemens Niederberger.
+% ----------------------------------------------------------------------------
+% If you have any ideas, questions, suggestions or bugs to report, please
+% feel free to contact me.
+% ----------------------------------------------------------------------------
+
+\@ifpackageloaded{xsim}{\endinput}{}
+\RequirePackage {expl3,xparse}
+\ExplSyntaxOn
+
+\tl_const:Nn \c_xsimverb_date_tl {2017/03/19}
+\tl_const:Nn \c_xsimverb_version_major_number_tl {0}
+\tl_const:Nn \c_xsimverb_version_minor_number_tl {1}
+\tl_const:Nn \c_xsimverb_version_subrelease_tl {}
+\tl_const:Nx \c_xsimverb_version_number_tl
+ {
+ \c_xsimverb_version_major_number_tl .
+ \c_xsimverb_version_minor_number_tl
+ }
+\tl_const:Nx \c_xsimverb_version_tl
+ {
+ \c_xsimverb_version_number_tl
+ \c_xsimverb_version_subrelease_tl
+ }
+\tl_const:Nn \c_xsimverb_info_tl {write environments verbatim to files}
+
+\ProvidesExplPackage
+ {xsimverb}
+ {\c_xsimverb_date_tl}
+ {\c_xsimverb_version_tl}
+ {\c_xsimverb_info_tl}
+
+% options, information
+
+\bool_new:N \g_xsim_final_bool
+\bool_new:N \g_xsim_verbose_bool
+\bool_new:N \g_xsim_clear_aux_bool
+
+\keys_define:nn {xsim}
+ {
+ final .bool_gset:N = \g_xsim_final_bool ,
+ verbose .bool_gset:N = \g_xsim_verbose_bool ,
+ clear-aux .bool_gset:N = \g_xsim_clear_aux_bool
+ }
+
+\DeclareOption {final}
+ { \bool_gset_true:N \g_xsim_final_bool }
+
+\DeclareOption {verbose}
+ { \bool_gset_true:N \g_xsim_verbose_bool }
+
+\DeclareOption {clear-aux}
+ { \bool_gset_true:N \g_xsim_clear_aux_bool }
+
+\ProcessOptions \scan_stop:
+
+% --------------------------------------------------------------------------
+
+% load module mechanism:
+\tl_set:Nn \l_tmpa_tl {code.tex}
+\@onefilewithoptions {xsim.modules} [][] \l_tmpa_tl
+
+% --------------------------------------------------------------------------
+
+\xsim_load_module:n {verbwrite}
+
+% --------------------------------------------------------------------------
+\file_input_stop:
+
+2018/01/31 - define xsim's options as dummy options to prevent possible errors
+ caused by undefined booleans