% ---------------------------------------------------------------------------- % the EXSHEETS package % % Yet another package for the creation of exercise sheets % % ---------------------------------------------------------------------------- % Clemens Niederberger % Web: http://www.mychemistry.eu/forums/forum/exsheets % E-Mail: contact@mychemistry.eu % ---------------------------------------------------------------------------- % Copyright 2011-2019 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_exsheets_date_tl {2019/09/30} \tl_const:Nn \c_exsheets_version_tl {0.21k} \tl_const:Nn \c_exsheets_info_tl {Yet~ another~ package~ for~ the~ creation~ of~ exercise~ sheets~ and~ exams.} \ProvidesExplPackage {exsheets} {\c_exsheets_date_tl} {\c_exsheets_version_tl} {\c_exsheets_info_tl} % ---------------------------------------------------------------------------- % variants of kernel functions: \cs_generate_variant:Nn \prop_get:NnN { NV , Nx , cV , cx } \cs_generate_variant:Nn \prop_get:NnNF { NV , Nx , cV , cx } \cs_generate_variant:Nn \prop_get:NnNT { NV , Nx , cV , cx } \cs_generate_variant:Nn \prop_get:NnNTF { NV , Nx , cV , cx } \cs_generate_variant:Nn \prop_gput:Nnn { Nf , Nff , Nfx , Nx , Nxx , NxV , cx , cxx } \cs_generate_variant:Nn \prop_gput_if_new:Nnn { Nxx } \cs_generate_variant:Nn \tl_if_blank:nTF { x } \cs_generate_variant:Nn \tl_if_eq:nnF { x } \cs_generate_variant:Nn \int_to_arabic:n { V } \cs_generate_variant:Nn \seq_set_split:Nnn { NnV } \cs_generate_variant:Nn \quark_if_no_value:nTF { V } % ---------------------------------------------------------------------------- % temporary variables \tl_new:N \l__exsheets_tmpa_tl \tl_new:N \l__exsheets_tmpb_tl \tl_new:N \l__exsheets_tmpc_tl \int_new:N \l__exsheets_tmpa_int \int_new:N \l__exsheets_tmpb_int \int_new:N \l__exsheets_tmpc_int \int_new:N \l__exsheets_tmpd_int \int_new:N \l__exsheets_tmpe_int \int_new:N \g__exsheets_tmpa_int \dim_new:N \l__exsheets_tmpa_dim \dim_new:N \l__exsheets_tmpb_dim \seq_new:N \l__exsheets_tmpa_seq \fp_new:N \l__exsheets_tmpa_fp \clist_new:N \l__exsheets_tmpa_clist \bool_new:N \l__exsheets_tmpa_bool % ---------------------------------------------------------------------------- % messages \msg_new:nnn {exsheets} {file-not-found} { You've~ requested~ the~ file~ `\tl_to_str:n {#1}'~ but~ I~ cannot~ find~ it~ \msg_line_context: . } \msg_new:nnn {exsheets} {totalpoints} { You~ need~ to~ activate~ `points/parse'~ if~ you~ want~ to~ use~ \token_to_str:N \totalpoints .~ I~ will~ do~ nothing~ instead. } \msg_new:nnn {exsheets} {parse-points} { You~ need~ to~ activate~ `points/parse'~ if~ you~ want~ to~ use~ \token_to_str:N #1 .~ I~ will~ do~ nothing~ instead. } \msg_new:nnn {exsheets} {headings} { You~ requested~ the~ headings~ instance~ `#1'~ \msg_line_context: \c_space_tl which~ is~ not~ defined.~ Is~ this~ a~ typo?~ Anyway,~ I~ will~ use~ the~ instance~ `block'~ instead~ and~pretend~ nothing~ happened. } \msg_new:nnn {exsheets} {random-file} { You~ asked~ me~ to~ select~ #1~ questions~ from~ file~ #2~ which~ contains~ #3~ questions.~ I'll~ select~ all~ of~ them. } \msg_new:nnn {exsheets} {random-selectable} { You~ asked~ me~ to~ select~ #1~ questions~ from~ file~ #2~ which~ contains~ #3~ questions.~ #4~ of~ these~ are~ selectable,~ so~ I'll~ select~ all~ of~ them. } \msg_new:nnn {exsheets} {only-inside-question} { The~ command~ `~ \token_to_str:N #1 \c_space_tl'~ can~ only~ be~ used~ inside~ the~ question~ environment. } \msg_new:nnn {exsheets} {loading-configurations} { Loading~ custom~ configurations~ file~ `exsheets_configurations.cfg'. } \msg_new:nnn {exsheets} {deprecated-command} { The~ command~ `#1'~ is~ deprecated.~ Use~ `#2'~ instead. } \msg_new:nnn {exsheets} {dropped-option} { The~ option~ `#1'~ has~ been~ dropped. } \msg_new:nnn {exsheets} {grade-missing} { The~ grade~ `#1'~ has~ never~ been~ declared. } \msg_new:nnn {exsheets} {grade-parse} { I~ cannot~ calculate~ the~ points~ for~ grade~ `#1'~ since~ you're~ using~ the~ option~ `points/parse=false'. } \msg_new:nnn {exsheets} {variations} { The~ number~ of~ variations~ must~ at~ least~ be~ `2'.~ You~ chose~ `#1'~ hence~ I'm~ doing~ nothing. } \msg_new:nnn {exsheets} {variant} { You~ must~ choose~ an~ integer~ between~ `1'~ and~ `#1'.~ You~ chose~ `#2'~ hence~ I'm~ doing~ nothing. } \cs_new_protected:Npn \exsheets_deprecate_cs:Npnn #1#2# { \__exsheets_deprecate_cs:Nnnn #1 {#2} } \cs_new_protected:Npn \__exsheets_deprecate_cs:Nnnn #1#2#3#4 { \cs_new_protected:Npn #1 #2 { \tl_if_blank:nTF {#3} { \msg_warning:nnnn {exsheets} { deprecated-command } {#1} {#4} } { \msg_warning:nnnn {exsheets} { deprecated-command } {#1} {#3} } #4 } } \cs_new_protected:Npn \exsheets_option_dropped:n #1 { \msg_warning:nnn {exsheets} {dropped-option} {#1} } % ---------------------------------------------------------------------------- \RequirePackage { xtemplate , l3keys2e , etoolbox , environ , pgfcore } % ---------------------------------------------------------------------------- % write to aux file: \cs_new_protected:Npn \exsheets_write_to_aux_x:n #1 { \if@filesw \iow_now:Nx \@auxout {#1} \fi } % prevent compilatin errors when exsheets is removed from a document: \AtBeginDocument{ \exsheets_write_to_aux_x:n { \exp_not:N \providecommand \exp_not:N \exsheets@question@property[3]{} ^^J \exp_not:N \providecommand \exp_not:N \exsheets@save@number[2]{} ^^J \exp_not:N \providecommand \exp_not:N \exsheets@sum@of@points[1]{} ^^J \exp_not:N \providecommand \exp_not:N \exsheets@sum@of@bonus[1]{} ^^J \exp_not:N \providecommand \exp_not:N \exsheets@used@id[2]{} ^^J } } % ---------------------------------------------------------------------------- % variables: \bool_new:N \l__exsheets_print_number_bool \bool_set_true:N \l__exsheets_print_number_bool \bool_new:N \l__exsheets_solutions_by_ref_bool \bool_new:N \l__exsheets_questions_totoc_bool \bool_new:N \l__exsheets_solutions_totoc_bool \bool_new:N \l__exsheets_auto_label_bool \bool_new:N \l__exsheets_no_skip_after_bool \bool_new:N \l__exsheets_points_questions_default_bool \bool_set_false:N \l__exsheets_points_questions_default_bool \bool_new:N \l__exsheets_parse_points_bool \bool_set_true:N \l__exsheets_parse_points_bool \bool_new:N \l__exsheets_points_name_bool \bool_set_true:N \l__exsheets_points_name_bool \bool_new:N \l__exsheets_points_separate_bonus_bool \bool_new:N \l__exsheets_grades_half_bool \bool_new:N \g__exsheets_questions_use_bool \bool_gset_true:N \g__exsheets_questions_use_bool \bool_new:N \l__exsheets_questions_use_bool \bool_set_true:N \l__exsheets_questions_use_bool \bool_new:N \l__exsheets_questions_deactivate_bool \bool_new:N \l__exsheets_save_question_body_to_aux_bool \bool_set_false:N \l__exsheets_save_question_body_to_aux_bool \bool_new:N \l__exsheets_solutions_use_bool \bool_set_true:N \l__exsheets_solutions_use_bool \bool_new:N \l__exsheets_use_this_question_bool \bool_new:N \l__exsheets_questions_print_bool \bool_set_true:N \l__exsheets_questions_print_bool \bool_new:N \l__exsheets_inside_question_bool \bool_new:N \l__exsheets_only_print_points_bool \bool_new:N \l__exsheets_exam_bool \bool_set_false:N \l__exsheets_exam_bool \bool_new:N \l__exsheets_questions_runin_bool \bool_set_false:N \l__exsheets_questions_runin_bool \bool_new:N \l__exsheets_questions_debug_bool \bool_set_false:N \l__exsheets_questions_debug_bool \bool_new:N \l__exsheets_select_questions_bool \bool_new:N \l__exsheets_include_all_bool \bool_new:N \l__exsheets_include_random_bool \bool_new:N \l__exsheets_include_by_id_bool \bool_set_true:N \l__exsheets_include_all_bool \bool_new:N \l__exsheets_include_questions_no_duplicates_bool \bool_new:N \l__exsheets_use_selection_bool \bool_new:N \l__exsheets_solutions_print_bool \bool_set_false:N \l__exsheets_solutions_print_bool \bool_new:N \l__exsheets_solutions_print_section_bool \bool_set_false:N \l__exsheets_solutions_print_section_bool \bool_new:N \l__exsheets_solutions_print_chapter_bool \bool_set_false:N \l__exsheets_solutions_print_chapter_bool \bool_new:N \l__exsheets_solutions_print_all_bool \bool_set_false:N \l__exsheets_solutions_print_all_bool \bool_new:N \l__exsheets_inside_solution_bool \bool_set_false:N \l__exsheets_inside_solution_bool \bool_new:N \l__exsheets_solutions_runin_bool \bool_set_false:N \l__exsheets_solutions_runin_bool \bool_new:N \l__exsheets_print_byID_sorted_bool \bool_set_true:N \l__exsheets_print_byID_sorted_bool \bool_new:N \l__exsheets_blank_width_bool \bool_new:N \l__exsheets_blank_linespread_bool \tl_new:N \l__exsheets_points_name_tl \tl_set:Nn \l__exsheets_points_name_tl {P.} \tl_new:N \l__exsheets_points_name_plural_tl \tl_set:Nn \l__exsheets_points_name_plural_tl {P.} \tl_new:N \l__exsheets_bonus_name_tl \tl_set:Nn \l__exsheets_bonus_name_tl {P.} \tl_new:N \l__exsheets_bonus_name_plural_tl \tl_set:Nn \l__exsheets_bonus_name_plural_tl {P.} \tl_new:N \l__exsheets_points_number_format_tl \tl_new:N \l__exsheets_bonus_number_format_tl \tl_new:N \l__exsheets_points_pre_bonus_marker_tl \tl_set:Nn \l__exsheets_points_pre_bonus_marker_tl { \space(+ } \tl_new:N \l__exsheets_points_post_bonus_marker_tl \tl_set:Nn \l__exsheets_points_post_bonus_marker_tl { ) } \tl_new:N \l__exsheets_points_format_tl \tl_set:Nn \l__exsheets_points_format_tl { \use:n } \tl_new:N \l__exsheets_qu_counter_pattern_tl \tl_set:Nn \l__exsheets_qu_counter_pattern_tl { qu. } \tl_new:N \l__exsheets_qu_counter_interpretation_tl \tl_new:N \l__exsheets_counter_patterns_tl \tl_new:N \l__exsheets_heading_instance_tl \tl_set:Nn \l__exsheets_heading_instance_tl {block} \tl_new:N \l__exsheets_question_heading_instance_tl \tl_new:N \l__exsheets_solution_heading_instance_tl \tl_new:N \l_exsheets_heading_title_question_format_tl \tl_new:N \l_exsheets_heading_title_solution_format_tl \tl_new:N \l_exsheets_heading_subtitle_question_format_tl \tl_new:N \l_exsheets_heading_subtitle_solution_format_tl \tl_new:N \l__exsheets_questions_toclevel_tl \tl_set:Nn \l__exsheets_questions_toclevel_tl {subsection} \tl_new:N \l__exsheets_solutions_toclevel_tl \tl_set:Nn \l__exsheets_solutions_toclevel_tl {subsection} \tl_new:N \l__exsheets_new_chapter_hook_tl \tl_new:N \l__exsheets_new_section_hook_tl \tl_new:N \g__exsheets_use_current_question_tl \tl_new:N \l__exsheets_questions_name_tl \tl_set:Nn \l__exsheets_questions_name_tl {Question} \tl_new:N \l__exsheets_exercise_name_tl \tl_set:Nn \l__exsheets_exercise_name_tl {Exercise} \tl_new:N \l__exsheets_questions_pre_hook_tl \tl_new:N \l__exsheets_questions_post_hook_tl \tl_new:N \l__exsheets_questions_pre_body_hook_tl \tl_new:N \l__exsheets_questions_post_body_hook_tl \tl_new:N \l__exsheets_after_begin_question_tl \tl_new:N \CurrentQuestionID \tl_new:N \l__exsheets_questions_id_tl \tl_new:N \l__exsheets_questions_subtitle_tl \tl_new:N \g__exsheets_questions_current_id_tl \tl_new:N \l__exsheets_questions_title_tl \tl_new:N \l__exsheets_questions_label_tl \tl_new:N \l__exsheets_questions_points_tl \tl_new:N \l__exsheets_include_id_tl \tl_new:N \l__exsheets_solutions_name_tl \tl_set:Nn \l__exsheets_solutions_name_tl {Solution} \tl_new:N \l__exsheets_solutions_pre_body_hook_tl \tl_new:N \l__exsheets_solutions_post_body_hook_tl \tl_new:N \l__exsheets_solutions_pre_hook_tl \tl_new:N \l__exsheets_solutions_post_hook_tl \tl_new:N \l__exsheets_blank_linespread_tl \tl_set:Nn \l__exsheets_blank_linespread_tl { 1 } \tl_new:N \l__exsheets_blank_scale_tl \tl_set:Nn \l__exsheets_blank_scale_tl { 1 } \tl_new:N \l__exsheets_use_solution_tl \tl_new:N \l_exsheets_solutions_name_style_tl \tl_set:Nn \l_exsheets_solutions_name_style_tl { \normalsize \bfseries } \int_zero_new:N \l__exsheets_variations_int \int_new:N \g__exsheets_questions_id_int \int_gzero:N \g__exsheets_questions_id_int \int_new:N \g__exsheets_questions_used_int \int_zero_new:N \l__exsheets_include_random_int \int_new:N \l__exsheets_questions_set_int \int_new:N \g__exsheets_select_random_int \int_new:N \g__exsheets_selection_number_int \int_new:N \l__exsheets_counter_ch_int \int_new:N \l__exsheets_current_ch_int \int_new:N \l__exsheets_counter_sec_int \int_new:N \l__exsheets_current_sec_int \int_new:N \l_exsheets_counter_qu_int \fp_new:N \g__exsheets_points_sum_fp \fp_new:N \l__exsheets_points_default_fp \fp_set:Nn \l__exsheets_points_default_fp {1} \fp_new:N \l__exsheets_question_points_fp \fp_new:N \g_exsheets_total_points_fp \fp_new:N \g__exsheets_bonus_sum_fp \fp_new:N \l__exsheets_question_bonus_fp \fp_new:N \g_exsheets_total_bonus_fp \fp_new:N \l__exsheets_grade_round_fp \fp_zero:N \l__exsheets_grade_round_fp \fp_new:N \g__exsheets_this_question_points_fp \fp_new:N \g__exsheets_this_question_bonus_fp \dim_new:N \l__exsheets_questions_skip_below_dim \dim_set:Nn \l__exsheets_questions_skip_below_dim { .5\baselineskip } \dim_new:N \l__exsheets_solutions_skip_below_dim \dim_set:Nn \l__exsheets_solutions_skip_below_dim { .5\baselineskip } \dim_new:N \l__exsheets_blank_dim \dim_new:N \l__exsheets_blank_line_increment_dim \dim_new:N \l__exsheets_blank_line_minimum_length_dim \seq_new:N \l__exsheets_use_tags_seq \seq_new:N \g_exsheets_included_questions_seq \prop_new:N \l__exsheets_relgrades_prop \prop_new:N \l__exsheets_class_prop \prop_new:N \g__exsheets_tags_prop \prop_new:N \g__exsheets_questions_id_prop \prop_new:N \g__exsheets_questions_subtitle_prop \prop_new:N \g__exsheets_questions_used_prop \prop_new:N \g_exsheets_question_identification_prop \prop_new:N \g__exsheets_included_questions_prop \prop_new:N \g__exsheets_solutions_content_prop \prop_new:N \g__exsheets_solutions_questions_id_prop \prop_new:N \g__exsheets_solutions_names_prop \prop_new:N \g__exsheets_solutions_counter_prop \clist_new:N \l__exsheets_include_id_clist \clist_new:N \l__exsheets_exclude_id_clist \clist_new:N \questionsincludedlast \box_new:N \l__exsheets_blank_box % ---------------------------------------------------------------------------- % how are questions/solutions/... counted? \RequirePackage {cntformats} \cs_if_exist:NT \thechapter { \AddCounterPattern* [exsheets] {chapter} {ch} \ReadCounterFrom [exsheets] {chapter} \l__exsheets_counter_ch_int } \AddCounterPattern* [exsheets] {section} {se} \ReadCounterFrom [exsheets] {section} \l__exsheets_counter_sec_int \NewCounterPattern* [exsheets] {question} {qu} \ReadCounterFrom [exsheets] {question} \l_exsheets_counter_qu_int % ---------------------------------------------------------------------------- % (also) package options: \keys_define:nn {exsheets} { counter-format .code:n = \SaveCounterPattern [exsheets] \l__exsheets_qu_counter_pattern_tl \l__exsheets_qu_counter_interpretation_tl {#1} , counter-format .initial:n = qu. , counter-within .code:n = \@addtoreset {question} {#1} , headings .tl_set:N = \l__exsheets_heading_instance_tl , load-headings .code:n = \exsheets_option_dropped:n {load-headings} , question/headings-format .tl_set:N = \l_exsheets_heading_title_question_format_tl , solution/headings-format .tl_set:N = \l_exsheets_heading_title_solution_format_tl , headings-format .meta:n = { question/headings-format = {#1} , solution/headings-format = {#1} } , headings-format .initial:n = \normalsize\bfseries , question/subtitle-format .tl_set:N = \l_exsheets_heading_subtitle_question_format_tl , solution/subtitle-format .tl_set:N = \l_exsheets_heading_subtitle_solution_format_tl , subtitle-format .meta:n = { question/subtitle-format = {#1} , solution/subtitle-format = {#1} } , subtitle-format .initial:n = \normalsize\itshape , load-tasks .code:n = \exsheets_option_dropped:n {load-tasks} , use-ref .bool_set:N = \l__exsheets_solutions_by_ref_bool , totoc .choice: , totoc / true .code:n = \bool_set_true:N \l__exsheets_questions_totoc_bool \bool_set_true:N \l__exsheets_solutions_totoc_bool , totoc / false .code:n = \bool_set_false:N \l__exsheets_questions_totoc_bool \bool_set_false:N \l__exsheets_solutions_totoc_bool , totoc .default:n = true , questions-totoc .bool_set:N = \l__exsheets_questions_totoc_bool , solutions-totoc .bool_set:N = \l__exsheets_solutions_totoc_bool , toc-level .code:n = \tl_set:Nn \l__exsheets_questions_toclevel_tl {#1} \tl_set:Nn \l__exsheets_solutions_toclevel_tl {#1} , questions-toc-level .tl_set:N = \l__exsheets_questions_toclevel_tl , solutions-toc-level .tl_set:N = \l__exsheets_solutions_toclevel_tl , skip-below .code:n = \dim_set:Nn \l__exsheets_questions_skip_below_dim {#1} \dim_set:Nn \l__exsheets_solutions_skip_below_dim {#1} , no-skip-below .bool_set:N = \l__exsheets_no_skip_after_bool , auto-label .bool_set:N = \l__exsheets_auto_label_bool , label-format .code:n = \cs_set:Npn \exsheets_label_format:n ##1 {#1} \exsheets_update_referencing_commands: , label-cmd .code:n = \cs_set:Npn \__exsheets_label:n {#1} \exsheets_update_referencing_commands: , ref-cmd .code:n = \cs_set:Npn \__exsheets_ref:n {#1} \exsheets_update_referencing_commands: , page-ref-cmd .code:n = \cs_set:Npn \__exsheets_pageref:n {#1} \exsheets_update_referencing_commands: , chapter-hook .tl_set:N = \l__exsheets_new_chapter_hook_tl , section-hook .tl_set:N = \l__exsheets_new_section_hook_tl %, % use-saved-counter-format .bool_set:N = \l__exsheets_use_saved_pattern_bool } % TODO: remove this in a v1.0: % process package options: \ProcessKeysOptions {exsheets} % ---------------------------------------------------------------------------- % points for exercises \cs_new_protected:Npn \__exsheets_set_points_name:nNN #1#2#3 { \tl_if_in:nnTF {#1} { / } { \__exsheets_set_points_name_aux:wNN #1 \q_mark #2#3 } { \__exsheets_set_points_name_aux:wNN #1 / \q_mark #2#3 } } \cs_new_protected:Npn \__exsheets_set_points_name_aux:wNN #1/#2 \q_mark #3#4 { \tl_set:Nn #3 {#1} \tl_set:Nn #4 { #1#2 } } \cs_new_protected:Npn \exsheets_set_points_name:n #1 { \__exsheets_set_points_name:nNN {#1} \l__exsheets_points_name_tl \l__exsheets_points_name_plural_tl } \cs_new_protected:Npn \exsheets_set_bonus_name:n #1 { \__exsheets_set_points_name:nNN {#1} \l__exsheets_bonus_name_tl \l__exsheets_bonus_name_plural_tl } % FIXME: \fp_add will deprecate! \cs_new_protected:Npn \exsheets_add_points:n #1 { \fp_gadd:Nn \g__exsheets_points_sum_fp {#1} \fp_gadd:Nn \g__exsheets_this_question_points_fp {#1} } \cs_generate_variant:Nn \exsheets_add_points:n { V } \cs_new_protected:Npn \exsheets_add_bonus:n #1 { \fp_gadd:Nn \g__exsheets_bonus_sum_fp {#1} \fp_gadd:Nn \g__exsheets_this_question_bonus_fp {#1} } \cs_generate_variant:Nn \exsheets_add_bonus:n { V } \cs_new_protected:Npn \exsheets@sum@of@points #1 { \fp_gset:Nn \g_exsheets_total_points_fp {#1} } \cs_new_protected:Npn \exsheets@sum@of@bonus #1 { \fp_gset:Nn \g_exsheets_total_bonus_fp {#1} } \cs_new:Npn \__exsheets_parse_points:nN #1#2 { \group_begin: \tl_use:N #2 { \bool_if:NTF \l__exsheets_parse_points_bool { \exsheets_num:n {#1} } { \use:n {#1} } } \group_end: } \cs_new:Npn \exsheets_parse_points:n #1 { \__exsheets_parse_points:nN {#1} \l__exsheets_points_number_format_tl } \cs_generate_variant:Nn \exsheets_parse_points:n { V } \cs_new:Npn \exsheets_parse_bonus:n #1 { \__exsheets_parse_points:nN {#1} \l__exsheets_bonus_number_format_tl } \cs_generate_variant:Nn \exsheets_parse_bonus:n { V } \cs_new:Npn \exsheets_num:n #1 { \fp_to_decimal:n {#1} } \cs_generate_variant:Nn \exsheets_num:n { V,x } \cs_new:Npn \__exsheets_print_points_name:nNN #1#2#3 { \bool_if:NT \l__exsheets_points_name_bool { \, \hbox:n { \bool_if:NTF \l__exsheets_parse_points_bool { \tl_if_eq:nnTF {#1} { ?? } { \tl_use:N #3 } { \fp_compare:nTF { #1 = 1 } { \tl_use:N #2 } { \tl_use:N #3 } } } { \tl_use:N #3 } } } } \cs_new:Npn \exsheets_points_name:n #1 { \__exsheets_print_points_name:nNN {#1} \l__exsheets_points_name_tl \l__exsheets_points_name_plural_tl } \cs_generate_variant:Nn \exsheets_points_name:n { V } \cs_new:Npn \exsheets_bonus_name:n #1 { \__exsheets_print_points_name:nNN {#1} \l__exsheets_bonus_name_tl \l__exsheets_bonus_name_plural_tl } \cs_generate_variant:Nn \exsheets_bonus_name:n { V } \NewDocumentCommand \addpoints { sm } { \bool_if:NTF \l__exsheets_inside_question_bool { \exsheets_add_points:n {#2} \IfBooleanF {#1} { \exsheets_print_points:n {#2} } } { \msg_error:nnn {exsheets} {only-inside-question} {\addpoints} } } \NewDocumentCommand \addbonus { sm } { \bool_if:NTF \l__exsheets_inside_question_bool { \exsheets_add_bonus:n {#2} \IfBooleanF {#1} { \exsheets_print_bonus:n {#2} } } { \msg_error:nnn {exsheets} {only-inside-question} {\addbonus} } } \NewDocumentCommand \points { sm } { \IfBooleanTF {#1} { \exsheets_parse_points:n {#2} } { \exsheets_print_points:n {#2} } } \NewDocumentCommand \bonus { sm } { \IfBooleanTF {#1} { \exsheets_parse_bonus:n {#2} } { \exsheets_print_bonus:n {#2} } } \cs_new_protected:Npn \exsheets_print_points:n #1 { \group_begin: \tl_use:N \l__exsheets_points_format_tl { \exsheets_parse_points:n {#1} \exsheets_points_name:n {#1} } \group_end: } \cs_generate_variant:Nn \exsheets_print_points:n { V } \cs_new_protected:Npn \exsheets_print_bonus:n #1 { \exsheets_parse_bonus:n {#1} \exsheets_bonus_name:n {#1} } \cs_generate_variant:Nn \exsheets_print_bonus:n { V } \NewDocumentCommand \pointssum { s } { \bool_if:NTF \l__exsheets_parse_points_bool { \IfBooleanTF {#1} { \exsheets_parse_points:n { \g_exsheets_total_points_fp } } { \exsheets_print_points:n { \g_exsheets_total_points_fp } } } { \msg_warning:nnn {exsheets} {parse-points} {\pointssum} } } \NewDocumentCommand \bonussum { s } { \bool_if:NTF \l__exsheets_parse_points_bool { \IfBooleanTF {#1} { \exsheets_parse_bonus:n { \g_exsheets_total_bonus_fp } } { \exsheets_print_bonus:n { \g_exsheets_total_bonus_fp } } } { \msg_warning:nnn {exsheets} {parse-points} {\bonussum} } } \NewDocumentCommand \currentpointssum { s } { \bool_if:NTF \l__exsheets_parse_points_bool { \IfBooleanTF {#1} { \exsheets_parse_points:n { \g__exsheets_points_sum_fp } } { \exsheets_print_points:n { \g__exsheets_points_sum_fp } } } { \msg_warning:nnn {exsheets} {parse-points} {\currentpointssum} } } \NewDocumentCommand \currentbonussum { s } { \bool_if:NTF \l__exsheets_parse_points_bool { \IfBooleanTF {#1} { \exsheets_parse_bonus:n { \g__exsheets_bonus_sum_fp } } { \exsheets_print_bonus:n { \g__exsheets_bonus_sum_fp } } } { \msg_warning:nnn {exsheets} {parse-points} {\currentbonussum} } } \NewDocumentCommand \totalpoints { s } { \group_begin: \tl_use:N \l__exsheets_points_format_tl { \exsheets_totalpoints: \IfBooleanF {#1} { \bool_if:nT { \l__exsheets_points_separate_bonus_bool || \fp_compare_p:n { \g_exsheets_total_bonus_fp = 0 } } { \exsheets_points_name:n { \g_exsheets_total_points_fp } } } \exsheets_totalbonus: \IfBooleanF {#1} { \fp_compare:nF { \g_exsheets_total_bonus_fp = 0 } { \bool_if:NTF \l__exsheets_points_separate_bonus_bool { \exsheets_bonus_name:n { \g_exsheets_total_bonus_fp } } { \exsheets_points_name:n { \g_exsheets_total_points_fp + \g_exsheets_total_bonus_fp } } } } } \group_end: } \exsheets_deprecate_cs:Npnn \sumpoints {} { \totalpoints* } \cs_new_protected:Npn \exsheets_totalpoints: { \bool_if:NTF \l__exsheets_parse_points_bool { \fp_compare:nTF { \g_exsheets_total_points_fp = 0 } { ?? \, \exsheets_points_name:n { ?? } } { \exsheets_parse_points:n { \g_exsheets_total_points_fp } } } { \msg_warning:nn {exsheets} {totalpoints} } } \cs_new:Npn \exsheets_totalbonus: { \bool_if:NT \l__exsheets_parse_points_bool { \fp_compare:nF { \g_exsheets_total_bonus_fp = 0 } { \tl_use:N \l__exsheets_points_pre_bonus_marker_tl \exsheets_parse_bonus:n { \g_exsheets_total_bonus_fp } \tl_use:N \l__exsheets_points_post_bonus_marker_tl } } } \keys_define:nn { exsheets / points } { name .code:n = \exsheets_set_points_name:n {#1} \exsheets_set_bonus_name:n {#1} , name-plural .code:n = \tl_set:Nn \l__exsheets_points_name_plural_tl {#1} \tl_set:Nn \l__exsheets_bonus_name_plural_tl {#1} , bonus-name .code:n = \exsheets_set_bonus_name:n {#1} , bonus-plural .tl_set:N = \l__exsheets_bonus_name_plural_tl , pre-bonus .tl_set:N = \l__exsheets_points_pre_bonus_marker_tl , post-bonus .tl_set:N = \l__exsheets_points_post_bonus_marker_tl , use-name .bool_set:N = \l__exsheets_points_name_bool , format .tl_set:N = \l__exsheets_points_format_tl , number-format .tl_set:N = \l__exsheets_points_number_format_tl , bonus-format .tl_set:N = \l__exsheets_bonus_number_format_tl , parse .bool_set:N = \l__exsheets_parse_points_bool , separate-bonus .bool_set:N = \l__exsheets_points_separate_bonus_bool } % ---------------------------------------------------------------------------- % variations of an exam, see http://tex.stackexchange.com/q/57012/5049 for % inspiration of the following features \cs_new_protected:Npn \exsheets_set_variations:n #1 { \int_compare:nNnTF {#1} < { 2 } { \msg_warning:nnn {exsheets} {variations} {#1} } { \int_set:Nn \l__exsheets_variations_int {#1} \int_zero:N \l__exsheets_tmpa_int \int_do_while:nNnn { \l__exsheets_tmpa_int } < { \l__exsheets_variations_int } { \int_incr:N \l__exsheets_tmpa_int \__exsheets_generate_variation_auxiliary:n { \l__exsheets_tmpa_int } \__exsheets_reset_variation_auxiliary:x { \int_use:N \l__exsheets_tmpa_int } } \__exsheets_generate_variation_auxiliary:n { \l__exsheets_variations_int + 1 } \exsheets_variant:n { 1 } } } \cs_new_protected:Npn \exsheets_variant:n #1 { \bool_if:nTF { \int_compare_p:nNn {#1} > { 0 } && \int_compare_p:nNn {#1} < { \l__exsheets_variations_int + 1 } } { \int_zero:N \l__exsheets_tmpa_int \int_do_while:nNnn { \l__exsheets_tmpa_int } < { \l__exsheets_variations_int } { \int_incr:N \l__exsheets_tmpa_int \__exsheets_reset_variation_auxiliary:x { \int_use:N \l__exsheets_tmpa_int } } \cs_set:cpn { __exsheets_variation_ \int_to_roman:n {#1} :n } ##1 { ##1 \cs_set:Npn \exsheets_last_variant: {##1} \use:c { __exsheets_variation_ \int_to_roman:n { #1 + 1 } :n } } } { \msg_warning:nnxx {exsheets} {variant} { \int_use:N \l__exsheets_variations_int } {#1} } } \cs_new_protected:Npn \exsheets_vary:w { \__exsheets_variation_i:n } \cs_new:Npn \exsheets_last_variant: { } \cs_new_protected:Npn \__exsheets_generate_variation_auxiliary:n #1 { \cs_if_exist:cTF { __exsheets_variation_ \int_to_roman:n {#1} :n } { \cs_set:cpn { __exsheets_variation_ \int_to_roman:n {#1} :n } { } } { \cs_new:cpn { __exsheets_variation_ \int_to_roman:n {#1} :n } { } } } \cs_new_protected:Npn \__exsheets_reset_variation_auxiliary:n #1 { \cs_set:cpn { __exsheets_variation_ \int_to_roman:n {#1} :n } ##1 { \use:c { __exsheets_variation_ \int_to_roman:n { #1 + 1 } :n } } } \cs_generate_variant:Nn \__exsheets_reset_variation_auxiliary:n { x } \NewDocumentCommand \SetVariations { m } { \exsheets_set_variations:n {#1} } \NewDocumentCommand \variant { m } { \exsheets_variant:n {#1} } \NewDocumentCommand \vary { } { \exsheets_vary:w } \NewDocumentCommand \lastvariant { } { \exsheets_last_variant: } % initiate: two variations, first is default \SetVariations { 2 } \variant { 1 } % ---------------------------------------------------------------------------- % grades distribution \cs_new:Npn \__exsheets_fp_round_to_half:n #1 { round( 2*(#1),0)/2 } \cs_new:Npn \__exsheets_grades_round:n #1 { round ( #1 , \l__exsheets_grade_round_fp ) } \cs_new:Npn \__exsheets_grade_rounded: { \bool_if:NTF { \l__exsheets_grades_half_bool } { \__exsheets_fp_round_to_half:n } { \__exsheets_grades_round:n } { \g_exsheets_total_points_fp * \l__exsheets_tmpa_fp } } \cs_new:Npn \exsheets_strip_braces:N #1 { \exp_after:wN \__exsheets_strip_braces:w \exp_after:wN #1#1 \q_stop } \cs_new_protected:Npn \__exsheets_strip_braces:w #1#2 \q_stop { \tl_set:Nn #1 {#2} } \cs_new_protected:Npn \__exsheets_declare_relgrades:n #1 { \clist_set:Nn \l__exsheets_tmpa_clist {#1} \clist_map_inline:Nn \l__exsheets_tmpa_clist { \__exsheets_declare_relgrade:w ##1 \q_stop } } \cs_new_protected:Npn \__exsheets_declare_relgrade:w #1 = #2 \q_stop { \tl_set:Nn \l__exsheets_tmpa_tl {#1} \tl_set:Nn \l__exsheets_tmpb_tl {#2} \tl_trim_spaces:N \l__exsheets_tmpa_tl \exsheets_strip_braces:N \l__exsheets_tmpa_tl \tl_trim_spaces:N \l__exsheets_tmpb_tl \exsheets_strip_braces:N \l__exsheets_tmpb_tl \prop_put:NVV \l__exsheets_relgrades_prop \l__exsheets_tmpa_tl \l__exsheets_tmpb_tl } \NewDocumentCommand \DeclareRelGrades { m } { \__exsheets_declare_relgrades:n {#1} } \@onlypreamble \DeclareRelGrades \cs_new_protected:Npn \__exsheets_grade:nn #1#2 { \prop_get:NnNTF \l__exsheets_relgrades_prop {#2} \l__exsheets_tmpa_fp { \bool_if:NTF \l__exsheets_parse_points_bool { \exsheets_parse_points:n { min( \__exsheets_grade_rounded: , \g_exsheets_total_points_fp ) } \int_compare:nT { #1 = 1 } { \exsheets_points_name:n { min( \__exsheets_grade_rounded: , \g_exsheets_total_points_fp ) } } } { \msg_warning:nnn {exsheets} {grade-parse} {#1} \textbf{??} } } { \msg_warning:nnn {exsheets} {grade-missing} {#1} \textbf{??} } } \NewDocumentCommand \grade { sm } { \IfBooleanTF {#1} { \__exsheets_grade:nn { 0 } {#2} } { \__exsheets_grade:nn { 1 } {#2} } } \keys_define:nn { exsheets / grades } { round .fp_set:N = \l__exsheets_grade_round_fp , half .bool_set:N = \l__exsheets_grades_half_bool } % ---------------------------------------------------------------------------- % Aufgaben und Lösungen \cs_new:Npn \exsheets_glue:N #1 { \cleaders \vbox:n {} \skip_vertical:N #1 } % classes, topics and other group concepts: \cs_new_protected:Npn \exsheets_new_question_class:nn #1#2 { \prop_put:Nnn \l__exsheets_class_prop {#2} {#1} \prop_new:c { g__exsheets_questions_#1_prop } \prop_new:c { g__exsheets_#2_prop } \prop_new:c { g__exsheets_#2_active_prop } \bool_new:c { l__exsheets_questions_#1_bool } \bool_new:c { g__exsheets_use_#2_bool } \bool_new:c { l__exsheets_#1_active_bool } \tl_new:c { l__exsheets_questions_#1_tl } \keys_define:nn {exsheets} { use-#2 .code:n = { \bool_gset_true:c { g__exsheets_use_#2_bool } \use:c { __exsheets_activate_#2:n } {##1} } , question / #1 .code:n = \bool_set_true:c { l__exsheets_questions_#1_bool } \tl_set:cn { l__exsheets_questions_#1_tl } {##1} } \cs_new_protected:cpn { __exsheets_activate_#2:n } ##1 { \prop_gclear:c { g__exsheets_#2_active_prop } \seq_set_split:Nnn \l__exsheets_tmpa_seq { , } {##1} \seq_map_inline:Nn \l__exsheets_tmpa_seq { \prop_gput:cnn { g__exsheets_#2_active_prop } {####1} { \__exsheets_dummy: } } } \cs_new_protected:cpn { __exsheets_questions_use_#2: } { % is there a #1 provided? \bool_if:cT { l__exsheets_questions_#1_bool } { % add #1 to questions property \prop_gput:cxx { g__exsheets_questions_#1_prop } { \int_to_arabic:n { \g__exsheets_questions_id_int } } { \exp_not:v { l__exsheets_questions_#1_tl } } % add to #2 list, if it's a new one \prop_get:cxNF { g__exsheets_#2_prop } { \exp_not:v { l__exsheets_questions_#1_tl } } \l__exsheets_tmpa_tl { \prop_gput:cxn { g__exsheets_#2_prop } { \exp_not:v { l__exsheets_questions_#1_tl } } { \__exsheets_dummy: } } } % are we using #2? \bool_if:cT { g__exsheets_use_#2_bool } { % is this question an active one? \prop_get:cxNTF { g__exsheets_#2_active_prop } { \exp_not:v { l__exsheets_questions_#1_tl } } \l__exsheets_tmpa_tl { \bool_set_true:c { l__exsheets_#1_active_bool } \bool_set_true:N \l__exsheets_use_this_question_bool } { \bool_set_false:c { l__exsheets_#1_active_bool } \bool_set_false:N \l__exsheets_questions_print_bool } } } \cs_new_protected:cpn { __exsheets_solutions_use_#2: } { % are we using #2? \bool_if:cT { g__exsheets_use_#2_bool } { % is the question to this solution an active one? % get question number from ID \prop_get:NVN \g__exsheets_questions_id_prop \g__exsheets_questions_current_id_tl \l__exsheets_tmpa_tl % get question #1 from number \prop_get:cVN { g__exsheets_questions_#1_prop } \l__exsheets_tmpa_tl \l__exsheets_tmpb_tl % test if #1 is active \prop_get:cVNF { g__exsheets_#2_active_prop } \l__exsheets_tmpb_tl \l__exsheets_tmpc_tl { \bool_set_false:N \l__exsheets_solutions_use_bool } % \bool_show:N \l__exsheets_solutions_use_bool } } } \cs_new_protected:Npn \exsheets_declare_question_class:nn #1#2 { \prop_if_in:NnTF \l__exsheets_class_prop {#2} {} { \exsheets_new_question_class:nn {#1} {#2} } } \NewDocumentCommand \DeclareQuestionClass { mm } { \exsheets_declare_question_class:nn {#1} {#2} } \@onlypreamble \DeclareQuestionClass % #1: class name \cs_new:Npn \exsheets_get_question_class:n #1 { \cs_if_exist:cT { l__exsheets_questions_#1_tl } { \tl_use:c { l__exsheets_questions_#1_tl } } } % #1: class name \DeclareExpandableDocumentCommand \GetQuestionClass { m } { \exsheets_get_question_class:n {#1} } % #1: class name \prg_new_conditional:Npnn \exsheets_if_question_class:n #1 {T,F,TF} { \tl_if_blank:fTF { \exsheets_get_question_class:n {#1} } { \prg_return_false: } { \prg_return_true: } } \cs_generate_variant:Nn \tl_if_blank:nTF {f} % #1: class name \cs_new_protected:Npn \exsheets_print_question_class:nTF #1#2#3 { \exsheets_if_question_class:nTF {#1} { \cs_set_protected:Npn \__exsheets_print_question_class:n ##1 {#2} \__exsheets_print_question_class:n { \exsheets_get_question_class:n {#1} } } {#3} } \NewDocumentCommand \PrintQuestionClassTF { mmm } { \exsheets_print_question_class:nTF {#1} {#2} {#3} } \NewDocumentCommand \PrintQuestionClassT { mm } { \exsheets_print_question_class:nTF {#1} {#2} { } } \NewDocumentCommand \PrintQuestionClassF { mm } { \exsheets_print_question_class:nTF {#1} { } {#2} } % ---------------------------------------------------------------------------- % question tags: % #1: ID % #2: tags \cs_new_protected:Npn \exsheets_set_question_tags:nn #1#2 { \prop_gput:Nnn \g__exsheets_tags_prop {#1} {#2} } \cs_generate_variant:Nn \exsheets_set_question_tags:nn {V} \cs_new_protected:Npn \exsheets_set_this_question_tags:n #1 { \exsheets_set_question_tags:Vn \g__exsheets_questions_current_id_tl {#1} } \keys_define:nn {exsheets} { use-tags .code:n = \seq_set_split:Nnn \l__exsheets_use_tags_seq {,} {#1} , question / tags .code:n = \exsheets_after_begin_question:n { \exsheets_set_this_question_tags:n {#1} } } % #1: ID \prg_new_protected_conditional:Npnn \exsheets_check_question_tags:n #1 {T,F,TF} { \bool_set_false:N \l__exsheets_tmpa_bool \seq_if_empty:NTF \l__exsheets_use_tags_seq { \bool_set_true:N \l__exsheets_tmpa_bool } { \prop_get:NnN \g__exsheets_tags_prop {#1} \l__exsheets_tmpa_tl \seq_map_inline:Nn \l__exsheets_use_tags_seq { \tl_if_in:NnT \l__exsheets_tmpa_tl {##1} { \bool_set_true:N \l__exsheets_tmpa_bool \seq_map_break: } } } \bool_if:NTF \l__exsheets_tmpa_bool { \prg_return_true: } { \prg_return_false: } } \cs_generate_variant:Nn \exsheets_check_question_tags:nF {V} % ---------------------------------------------------------------------------- % die 'question' Umgebung \cs_new:Npn \IfInsideQuestionTF { \bool_if:NTF \l__exsheets_inside_question_bool } \cs_new:Npn \IfInsideQuestionT { \bool_if:NT \l__exsheets_inside_question_bool } \cs_new:Npn \IfInsideQuestionF { \bool_if:NF \l__exsheets_inside_question_bool } \cs_new_eq:NN \exsheets_par: \par \cs_new_protected:Npn \exsheets_after_begin_question:n #1 { \tl_put_right:Nn \l__exsheets_after_begin_question_tl {#1} } \cs_new_protected:Npn \exsheets_add_space:N #1 { \bool_if:nF { % \l__exsheets_heading_runin_bool || \l__exsheets_heading_inline_bool || \l__exsheets_no_skip_after_bool } { \exsheets_par: \dim_compare:nNnT { \parskip } = { 0pt } { \exsheets_glue:N #1 } } } % #1: pre question hook % #2: post question hook \NewEnviron { __exsheets_questions_internal: } [2] { \__exsheets_start_question_if_used:n {#1} \bool_if:nT { % \l__exsheets_questions_print_bool && \g__exsheets_questions_use_bool && !\l__exsheets_questions_deactivate_bool } { \exsheets_if_current_question_is_used:T { % label: \tl_if_blank:VTF \l__exsheets_questions_label_tl { \bool_if:NT \l__exsheets_auto_label_bool { \exsheets_label:V \g__exsheets_questions_current_id_tl \exsheets_set_ref_properties:V \CurrentQuestionID } } { \exsheets_label:V \l__exsheets_questions_label_tl \exsheets_set_ref_properties:V \l__exsheets_questions_label_tl } \__exsheets_save_number_in:N \g_exsheets_question_identification_prop % TODO: don't print, only save if \l__exsheets_questions_print_bool is false \__exsheets_save_and_print_question_body:VVV \BODY \l__exsheets_questions_pre_body_hook_tl \l__exsheets_questions_post_body_hook_tl \exsheets_set_question_properties:x { \tl_if_blank:VF \l__exsheets_questions_subtitle_tl { subtitle = { \exp_not:V \l__exsheets_questions_subtitle_tl } , } counter = \l__exsheets_qu_counter_interpretation_tl , % number = \int_to_arabic:V \g__exsheets_questions_id_int , \bool_if:NF \l__exsheets_points_questions_default_bool { \fp_compare:nT { \g__exsheets_this_question_points_fp > 0 } { points = \fp_to_decimal:N \g__exsheets_this_question_points_fp , } \fp_compare:nT { \g__exsheets_this_question_bonus_fp > 0 } { bonus-points = \fp_to_decimal:N \g__exsheets_this_question_bonus_fp } } } \bool_if:NT \l__exsheets_questions_print_bool {#2} } } } % #1: body % #2: pre body hook % #3: post body hook \cs_new_protected:Npn \__exsheets_save_and_print_question_body:nnn #1#2#3 { \group_begin: \bool_if:NF \l__exsheets_save_question_body_to_aux_bool { \@fileswfalse } \exsheets_set_question_properties:n { question-body = {#1} } \group_end: \bool_if:NT \l__exsheets_questions_print_bool { #2 { #1 } #3 \exsheets_add_space:N \l__exsheets_questions_skip_below_dim } } \cs_generate_variant:Nn \__exsheets_save_and_print_question_body:nnn { VVV } \prg_new_conditional:Npnn \exsheets_if_question_subtitle: { p,T,F,TF } { \tl_if_blank:VTF \l__exsheets_questions_subtitle_tl { \prg_return_false: } { \prg_return_true: } } \cs_new:Npn \IfQuestionSubtitleTF { \exsheets_if_question_subtitle:TF } \cs_new:Npn \IfQuestionSubtitleT { \exsheets_if_question_subtitle:T } \cs_new:Npn \IfQuestionSubtitleF { \exsheets_if_question_subtitle:F } \cs_new_protected:Npn \__exsheets_read_points:w #1 ! #2 ! #3 \q_stop { \tl_if_blank:nTF {#1} { \bool_set_true:N \l__exsheets_only_print_points_bool \tl_set:Nn \l__exsheets_questions_points_tl {#2} } { \bool_set_false:N \l__exsheets_only_print_points_bool \__exsheets_read_points_aux:w #1++ \q_stop } } \cs_new_protected:Npn \__exsheets_read_points_aux:w #1+#2+#3 \q_stop { \tl_if_blank:nTF {#1} { \fp_zero:N \l__exsheets_question_points_fp } { \fp_set:Nn \l__exsheets_question_points_fp {#1} } \tl_if_blank:nTF {#2} { \fp_zero:N \l__exsheets_question_bonus_fp } { \tl_if_blank:nTF {#3} { \fp_zero:N \l__exsheets_question_bonus_fp } { \fp_set:Nn \l__exsheets_question_bonus_fp {#2} } } } \cs_new_protected:Npn \__exsheets_read_points:n #1 { \bool_if:NTF \l__exsheets_parse_points_bool { \__exsheets_read_points:w #1!! \q_stop } { \tl_set:Nn \l__exsheets_questions_points_tl {#1} } } \cs_new:Npn \exsheets_label_format:n #1 { qu:#1 } \cs_new:Npn \__exsheets_label:n { \label } \cs_new:Npn \exsheets_label:n { } \cs_generate_variant:Nn \exsheets_label:n { V } \cs_new:Npn \__exsheets_ref:n { \ref } \cs_new:Npn \exsheets_ref:n { } \cs_new:Npn \__exsheets_pageref:n { \pageref } \cs_new:Npn \exsheets_pageref:n { } \cs_new_protected:Npn \exsheets_update_referencing_command:NN #1#2 { \cs_set:Npx #1 ##1 { \exp_not:o {#2} { \exsheets_label_format:n {##1} } } } \cs_new_protected:Npn \exsheets_update_referencing_commands: { \exsheets_update_referencing_command:NN \exsheets_label:n \__exsheets_label:n \exsheets_update_referencing_command:NN \exsheets_ref:n \__exsheets_ref:n \exsheets_update_referencing_command:NN \exsheets_pageref:n \__exsheets_pageref:n } \cs_new_protected:Npn \exsheets_set_ref_properties:n #1 { \exsheets_set_question_properties:x { ref = \exp_not:o { \exsheets_ref:n {#1} } , pageref = \exp_not:o { \exsheets_pageref:n {#1} } } } \cs_generate_variant:Nn \exsheets_set_ref_properties:n { V } \exsheets_update_referencing_commands: \cs_new_protected:Npn \__exsheets_determine_question_usage: { \tl_gclear:N \g__exsheets_use_current_question_tl \prop_map_inline:Nn \l__exsheets_class_prop { \use:c { __exsheets_questions_use_##1: } \bool_if:nTF { ( \l__exsheets_use_this_question_bool && \bool_if_p:c { l__exsheets_##2_active_bool } ) || ! \bool_if_p:c { g__exsheets_use_##1_bool } } { \tl_gput_right:Nn \g__exsheets_use_current_question_tl { y } } { \tl_gput_right:Nn \g__exsheets_use_current_question_tl { n } } } } \prg_new_protected_conditional:Npnn \exsheets_if_current_question_is_used: { T,F,TF } { \tl_if_in:NnTF \g__exsheets_use_current_question_tl { n } { \prg_return_false: } { \prg_return_true: } } % #1: options \cs_new_protected:Npn \__exsheets_question_start:n #1 { \bool_set_true:N \l__exsheets_inside_question_bool \bool_if:NT \l__exsheets_questions_use_bool { \bool_gset_true:N \g__exsheets_questions_use_bool } % Optionen: \tl_if_blank:nF {#1} { \keys_set:nn { exsheets / question } {#1} } % ID: \__exsheets_determine_question_usage: \bool_if:nT { (\l__exsheets_questions_deactivate_bool && \l__exsheets_select_questions_bool) || !\l__exsheets_select_questions_bool } { \int_gincr:N \g__exsheets_questions_id_int \tl_if_blank:VTF \l__exsheets_questions_id_tl { \tl_gset:Nx \g__exsheets_questions_current_id_tl { \int_use:N \g__exsheets_questions_id_int } } { \tl_gset:Nx \g__exsheets_questions_current_id_tl { \tl_use:N \l__exsheets_questions_id_tl } } } \tl_use:N \l__exsheets_after_begin_question_tl \tl_clear:N \l__exsheets_after_begin_question_tl } % #1: points or blank \cs_new_protected:Npn \__exsheets_question_points:n #1 { % Punktevergabe: \fp_gzero:N \g__exsheets_this_question_points_fp \fp_gzero:N \g__exsheets_this_question_bonus_fp \tl_if_blank:nTF {#1} { \__exsheets_read_points:n { 0 } \bool_if:NT \l__exsheets_points_questions_default_bool { \fp_set_eq:NN \l__exsheets_question_points_fp \l__exsheets_points_default_fp % check if moving this to a later point has negative effects... % \exsheets_set_question_properties:x % { points = \fp_to_tl:N \l__exsheets_points_default_fp } } } { \fp_gset_eq:NN \g__exsheets_this_question_points_fp \l__exsheets_question_points_fp \fp_gset_eq:NN \g__exsheets_this_question_bonus_fp \l__exsheets_question_bonus_fp \__exsheets_read_points:n {#1} % check if moving this to a later point has negative effects... % \exsheets_set_question_properties:n { points = #2 } } } % #1: pre question hook \cs_new_protected:Npn \__exsheets_start_question_if_used:n #1 { \tl_if_blank:VT \l__exsheets_question_heading_instance_tl { \tl_set_eq:NN \l__exsheets_question_heading_instance_tl \l__exsheets_heading_instance_tl } % Auswahl der Frage: \__exsheets_select_question:V \g__exsheets_questions_current_id_tl \prop_map_inline:Nn \l__exsheets_class_prop { \use:c { __exsheets_questions_use_##1: } } \exsheets_check_question_tags:VF \g__exsheets_questions_current_id_tl { \bool_gset_false:N \g__exsheets_questions_use_bool } \bool_if:nT { \g__exsheets_questions_use_bool && !\l__exsheets_questions_deactivate_bool } { \exsheets_if_current_question_is_used:T { \refstepcounter {question} } \__exsheets_get_sectioning_numbers: \bool_if:nT { ( \l__exsheets_points_questions_default_bool && \l__exsheets_parse_points_bool ) || ( \l__exsheets_parse_points_bool% && % \l__exsheets_questions_print_bool ) } { \exsheets_if_current_question_is_used:T { \exsheets_add_points:V \l__exsheets_question_points_fp \exsheets_add_bonus:V \l__exsheets_question_bonus_fp } } \tl_if_blank:VTF \l__exsheets_questions_id_tl { \tl_set:Nx \l__exsheets_tmpa_tl { \int_use:N \g__exsheets_questions_id_int } } { \tl_set_eq:NN \l__exsheets_tmpa_tl \l__exsheets_questions_id_tl } \tl_set_eq:NN \CurrentQuestionID \l__exsheets_tmpa_tl \bool_if:NT \l__exsheets_questions_print_bool { \bool_if:NTF \l__exsheets_exam_bool { \tl_set_eq:NN \l__exsheets_questions_title_tl \l__exsheets_questions_name_tl } { \tl_set_eq:NN \l__exsheets_questions_title_tl \l__exsheets_exercise_name_tl } \bool_if:nTF { \l__exsheets_parse_points_bool && !\l__exsheets_only_print_points_bool } { #1 \exsheets_use_heading:VVVVnV \l__exsheets_question_heading_instance_tl \l__exsheets_questions_title_tl \l__exsheets_qu_counter_interpretation_tl \l__exsheets_question_points_fp { \l__exsheets_question_bonus_fp } \l__exsheets_tmpa_tl } { \bool_if:NT \l__exsheets_only_print_points_bool { \bool_set_false:N \l__exsheets_parse_points_bool } % might be dangerous to expand here... \tl_if_blank:xTF { \l__exsheets_questions_points_tl } { #1 \exsheets_use_heading:VVVnnV \l__exsheets_question_heading_instance_tl \l__exsheets_questions_title_tl \l__exsheets_qu_counter_interpretation_tl {0} {0} \l__exsheets_tmpa_tl } { #1 \exsheets_use_heading:VVVVnV \l__exsheets_question_heading_instance_tl \l__exsheets_questions_title_tl \l__exsheets_qu_counter_interpretation_tl \l__exsheets_questions_points_tl {0} \l__exsheets_tmpa_tl } } } } } \cs_generate_variant:Nn \__exsheets_start_question_if_used:n {V} \newenvironment { __exsheets_question: } [2] { \__exsheets_question_start:n {#1} \__exsheets_question_points:n {#2} \exp_args:NVV \__exsheets_questions_internal: \l__exsheets_questions_pre_hook_tl \l__exsheets_questions_post_hook_tl } { \end__exsheets_questions_internal: } % current question number: \cs_new_protected:Npn \exsheets@save@number #1#2 { \expandafter\global\expandafter\edef\csname exsheets@question@#1\endcsname{#2} } \cs_new_protected:Npn \__exsheets_save_number_in_aux_x:Nnn #1#2#3 { \prop_gput:Nfx #1 {#2} {#3} \exsheets_write_to_aux_x:n { \exsheets@save@number {#2} {#3} } } \cs_new_protected:Npn \__exsheets_save_number_in:N #1 { \bool_if:NTF \l__exsheets_print_number_bool { \cs_if_exist:NTF \thechapter { \__exsheets_save_number_in_aux_x:Nnn #1 { \int_use:N \g__exsheets_questions_id_int } { \arabic{chapter}-\arabic{section}-\arabic{question} } } { \__exsheets_save_number_in_aux_x:Nnn #1 { \int_use:N \g__exsheets_questions_id_int } { -\arabic{section}-\arabic{question} } } } { \cs_if_exist:NTF \thechapter { \__exsheets_save_number_in_aux_x:Nnn #1 { \int_use:N \g__exsheets_questions_id_int } { @@@\arabic{chapter}-\arabic{section}-\arabic{question} } } { \__exsheets_save_number_in_aux_x:Nnn #1 { \int_use:N \g__exsheets_questions_id_int } { @@@-\arabic{section}-\arabic{question} } } } } % #1: prop % #2: id % #3: code \cs_new_protected:Npn \__exsheets_restore_number_from_and_do:Nnn #1#2#3 { \prop_get:NnNT #1 {#2} \l__exsheets_tmpc_tl { \exp_after:wN \__exsheets_read_number:w \l__exsheets_tmpc_tl \q_stop #3 } % use \exsheets_read_counter_settings:V \l__exsheets_qu_counter_format_tl % afterwards to print the number } \cs_generate_variant:Nn \__exsheets_restore_number_from_and_do:Nnn { NV } \DeclareExpandableDocumentCommand \QuestionNumber {m} { \exsheets_question_number:n {#1} } \cs_new:Npn \exsheets_question_number:n #1 { \exsheets_get_question_property:nn {counter} {#1} } % \cs_new_protected:Npn \__exsheets_question_number:N #1 % { % \cs_if_exist:NTF #1 % { % \exp_after:wN \__exsheets_get_question_number:w #1 \q_stop % \ReadCounterPatternFrom [exsheets] \l__exsheets_qu_counter_pattern_tl % } % { \textbf {??} } % } % \cs_generate_variant:Nn \__exsheets_question_number:N { c } % \cs_new_protected:Npn \__exsheets_get_question_number:w #1-#2-#3 \q_stop % { % \cs_if_exist:NT \thechapter % { \int_set:Nn \l__exsheets_counter_ch_int {#1} } % \int_set:Nn \l__exsheets_counter_sec_int {#2} % \int_set:Nn \l_exsheets_counter_qu_int {#3} % } % map ID's to number of used questions \cs_new_protected:Npn \exsheets@used@id #1#2 { \prop_gput:Nnn \g__exsheets_questions_used_prop {#1} {#2} } \cs_new_protected:Npn \__exsheets_mark_as_used:n #1 { \int_gincr:N \g__exsheets_questions_used_int \prop_gput_if_new:Nxx \g__exsheets_questions_used_prop {#1} { \int_use:N \g__exsheets_questions_used_int } \exsheets_write_to_aux_x:n { \exsheets@used@id {#1} { \int_use:N \g__exsheets_questions_used_int } } } % ---------------------------------------------------------------------------- % title of a question: \cs_if_exist:NF \exsheets_headings_files_loaded: { \file_input:n {exsheets_headings.def} \file_input:n {exsheets_headings.cfg} } % #1: instance % #2: title % #3: number % #4: points % #5: bonus % #6: id \cs_new_protected:Npn \exsheets_use_heading:nnnnnn #1#2#3#4#5#6 { \IfInstanceExistTF {exsheets-heading} {#1} { \UseInstance {exsheets-heading} {#1} } { \msg_warning:nnx {exsheets} {headings} {#1} \UseInstance {exsheets-heading} {block} } {#2} {#3} {#4} {#5} {#6} } \cs_generate_variant:Nn \exsheets_use_heading:nnnnnn { V , VVVnnV , VVVVnV } \NewDocumentCommand \ExSheetsHeading { mmmmmm } { \exsheets_use_heading:nnnnnn {#1} {#2} {#3} {#4} {#5} {#6} } % options for the questions: \keys_define:nn { exsheets / question } { type .choice: , type / exam .code:n = \bool_set_true:N \l__exsheets_exam_bool , type / exercise .code:n = \bool_set_false:N \l__exsheets_exam_bool , name .code:n = \tl_set:Nn \l__exsheets_exercise_name_tl {#1} \tl_set:Nn \l__exsheets_questions_name_tl {#1} , headings .tl_set:N = \l__exsheets_question_heading_instance_tl , subtitle .tl_set:N = \l__exsheets_questions_subtitle_tl , print .bool_set:N = \l__exsheets_questions_print_bool , print .default:n = true , ID .tl_set:N = \l__exsheets_questions_id_tl , label .tl_set:N = \l__exsheets_questions_label_tl , use .choice: , use / true .code:n = \bool_gset_true:N \g__exsheets_questions_use_bool \bool_set_true:N \l__exsheets_questions_use_bool , use / false .code:n = \bool_gset_false:N \g__exsheets_questions_use_bool \bool_set_false:N \l__exsheets_questions_use_bool , use .default:n = true , skip-below .dim_set:N = \l__exsheets_questions_skip_below_dim , no-skip-below .bool_set:N = \l__exsheets_no_skip_after_bool , pre-hook .tl_set:N = \l__exsheets_questions_pre_hook_tl , post-hook .tl_set:N = \l__exsheets_questions_post_hook_tl , pre-body-hook .tl_set:N = \l__exsheets_questions_pre_body_hook_tl , post-body-hook .tl_set:N = \l__exsheets_questions_post_body_hook_tl , save-to-aux .bool_set:N = \l__exsheets_save_question_body_to_aux_bool } % ---------------------------------------------------------------------------- % using ``metadata'': \cs_new_protected:Npn \exsheets_declare_question_property:n #1 { \prop_new:c { g__exsheets_question_property_#1_prop } \keys_define:nn { question / meta } { #1 .code:n = \__exsheets_question_set_property:nn {#1} {##1} } } \NewDocumentCommand \DeclareQuestionProperty { m } { \exsheets_declare_question_property:n {#1} } \@onlypreamble \DeclareQuestionProperty \cs_new_protected:Npn \exsheets_set_question_properties:n #1 { \@bsphack \bool_if:NTF \l__exsheets_inside_question_bool { \keys_set:nn { question / meta } {#1} } { \msg_error:nnn {exsheets} {only-inside-question} {\SetQuestionProperties} } \@esphack } \cs_generate_variant:Nn \exsheets_set_question_properties:n { x } % #1: property % #2: ID \prg_new_conditional:Npnn \exsheets_if_question_property:nn #1#2 {T,F,TF} { \prop_if_in:cnTF { g__exsheets_question_property_#1_prop } {#2} { \prg_return_true: } { \prg_return_false: } } \DeclareExpandableDocumentCommand \IfQuestionPropertyTF { } { \exsheets_if_question_property:nnTF } \DeclareExpandableDocumentCommand \IfQuestionPropertyT { } { \exsheets_if_question_property:nnT } \DeclareExpandableDocumentCommand \IfQuestionPropertyF { } { \exsheets_if_question_property:nnF } \NewDocumentCommand \SetQuestionProperties { +m } { \exsheets_set_question_properties:n {#1} } % #1: ID % #2: property % #3: value \cs_new_protected:Npn \exsheets@question@property #1#2#3 { \prop_gput:cnn { g__exsheets_question_property_#2_prop } {#1} {#3} } % #1: property % #2: value \cs_new_protected:Npn \__exsheets_question_set_property:nn #1#2 { \prop_gput:cVn { g__exsheets_question_property_#1_prop } \CurrentQuestionID {#2} % watch if this has negativ effects: \exsheets_write_to_aux_x:n { \exp_not:N \exsheets@question@property { \CurrentQuestionID } { \exp_not:n {#1} } { \exp_not:n {#2} } } } % #1: property % #2: ID \DeclareExpandableDocumentCommand \GetQuestionProperty { mm } { \exsheets_get_question_property:no {#1} {#2} } \cs_new:Npn \exsheets_get_question_property:nn #1#2 { \prop_if_in:cnTF { g__exsheets_question_property_#1_prop } {#2} { \prop_item:cn { g__exsheets_question_property_#1_prop } {#2} } { ?? } } \cs_generate_variant:Nn \exsheets_get_question_property:nn { no , nV } \cs_new_protected:Npn \exsheets_for_each_question_do:n #1 { \int_gzero:N \g__exsheets_tmpa_int \prop_map_inline:Nn \g__exsheets_questions_used_prop { \int_gincr:N \g__exsheets_tmpa_int #1 } } \NewDocumentCommand \ForEachQuestion {m} { \exsheets_for_each_question_do:n {#1} } \cs_new:Npn \iflastquestion { \int_compare:nTF { \numberofquestions = \g__exsheets_tmpa_int } } \cs_if_exist:NF \numberofquestions { \cs_new:Npn \numberofquestions { \int_to_arabic:V \g__exsheets_questions_used_int } } % ---------------------------------------------------------------------------- % debug-Info: \RequirePackage {xcolor} \colorlet {exsheetsdebugcolor} {yellow} \keys_define:nn {exsheets} { debug .bool_set:N = \l__exsheets_questions_debug_bool } \cs_new_protected:Npn \exsheets_debug_box:nn #1#2 { \par \noindent \group_begin: \normalfont\normalsize\normalcolor \colorbox {exsheetsdebugcolor} { \parbox { \dim_eval:n { #1 - 2 \fboxsep } } {#2} } \group_end: \par } \cs_new:Npn \__exsheets_list_comma: {} \cs_new_protected:Npn \exsheets_questions_debug:n #1 { \bool_if:nT { \l__exsheets_questions_debug_bool && \l__exsheets_inside_question_bool } { \exsheets_debug_box:nn { \linewidth } { \textbf {ID} :~ \textit {#1} \prop_map_inline:Nn \l__exsheets_class_prop { \bool_if:cT { l__exsheets_questions_##2_bool } { ;~ \textbf {##2} :~ \textit { \tl_use:c { l__exsheets_questions_##2_tl } } } } \prop_get:NnN \g__exsheets_tags_prop {#1} \l__exsheets_tmpa_tl \quark_if_no_value:VTF \l__exsheets_tmpa_tl { ;~ \textbf {not~tagged} } { ;~ \textbf {tags} : ~ \seq_set_split:NnV \l__exsheets_tmpa_seq {,} \l__exsheets_tmpa_tl \textit { \seq_use:Nn \l__exsheets_tmpa_seq {,~} } } } } } \exsheets_deprecate_cs:Npnn \DebugExSheets #1 {the~ option~ debug} { \keys_set:nn {exsheets} { debug = #1 } } % ---------------------------------------------------------------------------- % include random/selected questions from a file: \cs_new_protected:Npn \exsheets_file_input_if_exist:n #1 { \file_if_exist:nT {#1} { \file_input:n {#1} } } \cs_new_protected:Npn \exsheets_file_input_if_exist:nF #1#2 { \file_if_exist:nTF {#1} { \file_input:n {#1} } {#2} } \cs_new_protected:Npn \exsheets_prop_count:N #1 { \__exsheets_prop_count:NN #1 \l__exsheets_tmpa_int \int_use:N \l__exsheets_tmpa_int } \cs_new_protected:Npn \__exsheets_prop_count:NN #1#2 { \int_zero:N #2 \prop_map_inline:Nn #1 { \int_incr:N #2 } } \NewDocumentCommand \includequestions { om } { \group_begin: \IfNoValueF {#1} { \keys_set:nn { exsheets / include } {#1} } \exsheets_include_questions:n {#2} \group_end: } \cs_new_protected:Npn \exsheets_include_questions:n #1 { \bool_set_true:N \l__exsheets_select_questions_bool \clist_gclear:N \questionsincludedlast \seq_set_split:Nnn \l__exsheets_tmpa_seq { , } {#1} \seq_map_inline:Nn \l__exsheets_tmpa_seq { \bool_set_true:N \l__exsheets_questions_deactivate_bool \int_set_eq:NN \l__exsheets_questions_set_int \g__exsheets_questions_used_int \int_set_eq:NN \l__exsheets_tmpc_int \g__exsheets_questions_id_int \int_gzero:N \g__exsheets_select_random_int \exsheets_file_input_if_exist:nF {##1} { \msg_error:nnn {exsheets} {file-not-found} {##1} } \__exsheets_prop_count:NN \g__exsheets_included_questions_prop \l__exsheets_tmpa_int \bool_if:NT \l__exsheets_questions_debug_bool { \exsheets_debug_box:nn { \linewidth } { The~file~ ` \texttt { \tl_to_str:n {##1} } ' ~contains~ \exsheets_prop_count:N \g__exsheets_included_questions_prop \c_space_tl questions~with~the~following~IDs:\par \cs_set_protected:Npn \__exsheets_list_comma: { \cs_set:Npn \__exsheets_list_comma: { ,~ } } \prop_map_inline:Nn \g__exsheets_included_questions_prop { \__exsheets_list_comma: ####2 } } } \bool_set_false:N \l__exsheets_questions_deactivate_bool \int_set_eq:NN \l__exsheets_tmpd_int \g__exsheets_questions_id_int \int_gset_eq:NN \g__exsheets_questions_id_int \l__exsheets_tmpc_int \bool_if:NT \l__exsheets_include_questions_no_duplicates_bool { \seq_map_inline:Nn \g_exsheets_included_questions_seq { \clist_put_right:Nn \l__exsheets_exclude_id_clist {####1} } } \__exsheets_select_question_random:n {##1} \exsheets_file_input_if_exist:n {##1} \int_gset_eq:NN \g__exsheets_questions_id_int \l__exsheets_tmpd_int \prop_gclear:N \g__exsheets_included_questions_prop \clist_clear:N \l__exsheets_include_id_clist \clist_clear:N \l__exsheets_exclude_id_clist \int_gzero:N \g__exsheets_selection_number_int % \seq_show:N \g_exsheets_included_questions_seq } \bool_set_false:N \l__exsheets_select_questions_bool } % options: % - IDs= % - all=true|false % default when no option is given % - random= % - exclude= \keys_define:nn { exsheets / include } { all .bool_set:N = \l__exsheets_include_all_bool , IDs .code:n = \bool_set_false:N \l__exsheets_include_all_bool \bool_set_false:N \l__exsheets_include_random_bool \bool_set_true:N \l__exsheets_include_by_id_bool \clist_set:Nn \l__exsheets_include_id_clist {#1} , % the `exclude' option does _not_ set \l__exsheets_include_random_bool , % \l__exsheets_include_by_id_bool or \l__exsheets_include_all_bool so % it can be combined with the other options: exclude .clist_set:N = \l__exsheets_exclude_id_clist , random .code:n = \bool_set_false:N \l__exsheets_include_all_bool \bool_set_true:N \l__exsheets_include_random_bool \bool_set_false:N \l__exsheets_include_by_id_bool \int_set:Nn \l__exsheets_include_random_int {#1} , no-duplicates .bool_set:N = \l__exsheets_include_questions_no_duplicates_bool } \cs_new_protected:Npn \__exsheets_select_question:n #1 { \bool_if:NTF \l__exsheets_select_questions_bool { \bool_if:NTF \l__exsheets_questions_deactivate_bool { \bool_if:NT \l__exsheets_questions_use_bool { \int_gincr:N \g__exsheets_selection_number_int \bool_if:NTF \l__exsheets_include_random_bool { \prop_gput:Nxn \g__exsheets_included_questions_prop { \int_use:N \g__exsheets_selection_number_int } {#1} } { \prop_gput:Nxn \g__exsheets_included_questions_prop { \int_use:N \g__exsheets_questions_id_int } {#1} } } } { \int_gincr:N \g__exsheets_questions_id_int \bool_if:NTF \l__exsheets_include_random_bool { \int_gincr:N \g__exsheets_select_random_int \int_set_eq:NN \l__exsheets_tmpa_int \g__exsheets_select_random_int % ID: \int_use:N \g__exsheets_questions_id_int ,~ % set: \int_use:N \l__exsheets_questions_set_int ,~ % selection: \int_use:N \g__exsheets_selection_number_int ,~ % tmpa: \int_use:N \l__exsheets_tmpa_int \par } { \int_set_eq:NN \l__exsheets_tmpa_int \g__exsheets_questions_id_int } \prop_get:NxN \g__exsheets_included_questions_prop { \int_use:N \l__exsheets_tmpa_int } \l__exsheets_tmpa_tl \__exsheets_select_question_by_id:V \l__exsheets_tmpa_tl } } { \__exsheets_use_question:n {#1} } } \cs_generate_variant:Nn \__exsheets_select_question:n { V } \cs_new_protected:Npn \__exsheets_select_question_by_id:n #1 { \bool_if:NTF \l__exsheets_include_all_bool {% parsing list of excluded IDs \clist_if_in:NnTF \l__exsheets_exclude_id_clist {#1} { \bool_gset_false:N \g__exsheets_questions_use_bool } { \bool_gset_true:N \g__exsheets_questions_use_bool \clist_gput_right:Nn \questionsincludedlast {#1} \seq_gput_right:Nn \g_exsheets_included_questions_seq {#1} \__exsheets_use_question:n {#1} } } {% parsing list of included IDs \clist_if_in:NnTF \l__exsheets_include_id_clist {#1} { \clist_if_in:NnTF \l__exsheets_exclude_id_clist {#1} { \bool_gset_false:N \g__exsheets_questions_use_bool } { \bool_gset_true:N \g__exsheets_questions_use_bool \clist_gput_right:Nn \questionsincludedlast {#1} \seq_gput_right:Nn \g_exsheets_included_questions_seq {#1} \__exsheets_use_question:n {#1} % \exsheets_if_current_question_is_used:T % { \__exsheets_mark_as_used:n {#1} } % \prop_gput:NnV \g__exsheets_questions_id_prop % {#1} % \g__exsheets_questions_id_int } } { \bool_gset_false:N \g__exsheets_questions_use_bool } } } \cs_generate_variant:Nn \__exsheets_select_question_by_id:n { V } \cs_new_protected:Npn \__exsheets_use_question:n #1 { \exsheets_if_current_question_is_used:T { \__exsheets_mark_as_used:n {#1} } \prop_gput:NnV \g__exsheets_questions_id_prop {#1} \g__exsheets_questions_id_int } % TODO: poor efficiency -- there should be no need to map through % \l__exsheets_class_prop *two* times \cs_new_protected:Npn \__exsheets_select_question_random:n #1 { \bool_if:NT \l__exsheets_include_random_bool { % number of question in file: \__exsheets_prop_count:NN \g__exsheets_included_questions_prop \l__exsheets_tmpa_int % number of selectable questions in file: \int_zero:N \l__exsheets_tmpb_int \prop_map_inline:Nn \g__exsheets_included_questions_prop { \prop_map_inline:Nn \l__exsheets_class_prop { \bool_if:cT { g__exsheets_use_####1_bool } { \bool_set_true:N \l__exsheets_use_selection_bool \prop_get:cnNT { g__exsheets_questions_####2_prop } {##1} \l__exsheets_tmpa_tl { \prop_if_in:cVT { g__exsheets_####1_active_prop } \l__exsheets_tmpa_tl { \int_incr:N \l__exsheets_tmpb_int } } } } } \int_compare:nTF { \l__exsheets_include_random_int >= \l__exsheets_tmpa_int } { % not enough questions available: \msg_warning:nnxxx {exsheets} {random-file} { \int_use:N \l__exsheets_include_random_int } {#1} { \int_use:N \l__exsheets_tmpa_int } \bool_set_true:N \l__exsheets_include_all_bool } { \bool_if:nTF { \int_compare_p:n { \l__exsheets_include_random_int >= \l__exsheets_tmpb_int } && \l__exsheets_use_selection_bool } { % not enough questions selectable: \msg_warning:nnxxxx {exsheets} {random-selectable} { \int_use:N \l__exsheets_include_random_int } {#1} { \int_use:N \l__exsheets_tmpa_int } { \int_use:N \l__exsheets_tmpb_int } \bool_set_true:N \l__exsheets_include_all_bool } { \int_zero:N \l__exsheets_tmpb_int \int_do_while:nn { \l__exsheets_tmpb_int < \l__exsheets_include_random_int } { \pgfmathparse { random(\int_use:N \l__exsheets_tmpa_int) } \tl_set_eq:NN \l__exsheets_tmpa_tl \pgfmathresult \prop_get:NVN \g__exsheets_included_questions_prop \l__exsheets_tmpa_tl \l__exsheets_tmpb_tl \clist_if_in:NVF \l__exsheets_include_id_clist \l__exsheets_tmpb_tl { \tl_clear:N \l__exsheets_include_id_tl \prop_map_inline:Nn \l__exsheets_class_prop { \prop_get:cVNT { g__exsheets_questions_##2_prop } \l__exsheets_tmpa_tl \l__exsheets_tmpc_tl { \bool_if:cT { g__exsheets_use_##1_bool } { \prop_if_in:cVTF { g__exsheets_##1_active_prop } \l__exsheets_tmpc_tl { \tl_put_right:Nn \l__exsheets_include_id_tl {y} } { \tl_put_right:Nn \l__exsheets_include_id_tl {n} } } } } \bool_set_true:N \l__exsheets_tmpa_bool \clist_if_in:NVT \l__exsheets_exclude_id_clist \l__exsheets_tmpb_tl { \bool_set_false:N \l__exsheets_tmpa_bool } \seq_if_in:NnT \g_exsheets_included_questions_seq \l__exsheets_tmpb_tl { \bool_set_false:N \l__exsheets_tmpa_bool } \tl_if_in:NnT \l__exsheets_include_id_tl {n} { \bool_set_false:N \l__exsheets_tmpa_bool } \bool_if:NT \l__exsheets_tmpa_bool % \tl_if_in:NnF \l__exsheets_include_id_tl { n } % { % \clist_if_in:NVF % \l__exsheets_exclude_id_clist % \l__exsheets_tmpb_tl { \clist_put_right:NV \l__exsheets_include_id_clist \l__exsheets_tmpb_tl \int_incr:N \l__exsheets_tmpb_int } % } } } } } } } % \cs_generate_variant:Nn \clist_if_in_p:Nn {NV} % \cs_generate_variant:Nn \seq_if_in_p:Nn {NV} \NewDocumentCommand \PrintIfIncludeActiveTF { mm } { \bool_if:NTF \l__exsheets_questions_deactivate_bool {#2} {#1} } \NewDocumentCommand \PrintIfIncludeActiveT { } { \bool_if:NF \l__exsheets_questions_deactivate_bool } \NewDocumentCommand \PrintIfIncludeActiveF { } { \bool_if:NT \l__exsheets_questions_deactivate_bool } % ---------------------------------------------------------------------------- % die 'solution' Umgebung: \cs_new_protected:Npn \__exsheets_save_solution:n #1 { % save for later use \bool_if:NTF \l__exsheets_include_all_bool { \int_set:Nn \l__exsheets_tmpe_int { \g__exsheets_questions_id_int -1 } } { \int_set_eq:NN \l__exsheets_tmpe_int \g__exsheets_questions_id_int } \prop_gput:Nxn \g__exsheets_solutions_content_prop { \int_use:N \g__exsheets_questions_id_int } {#1} \prop_gput:NxV \g__exsheets_solutions_questions_id_prop { \int_use:N \g__exsheets_questions_id_int } \g__exsheets_questions_current_id_tl \prop_gput:NxV \g__exsheets_solutions_names_prop { \int_use:N \g__exsheets_questions_id_int } \l__exsheets_solutions_name_tl \prop_gput:NxV \g__exsheets_solutions_counter_prop { \int_use:N \g__exsheets_questions_id_int } \l__exsheets_qu_counter_pattern_tl % print here \exsheets_if_current_question_is_used:T { \bool_if:NT \l__exsheets_solutions_print_bool { \int_set:Nn \l_exsheets_counter_qu_int { \arabic {question} } \__exsheets_get_sectioning_numbers: \__exsheets_print_solution:VVn \g__exsheets_questions_current_id_tl \l__exsheets_solutions_name_tl {#1} } } } \cs_generate_variant:Nn \__exsheets_save_solution:n { V } \NewEnviron { __exsheets_solution_internal: } { \bool_if:nT { \g__exsheets_questions_use_bool && \l__exsheets_solutions_use_bool } { \__exsheets_save_solution:V \BODY } } \newenvironment {__exsheets_solution:} [1] { \tl_clear:N \l__exsheets_use_solution_tl % \prop_show:N \l__exsheets_class_prop \prop_map_inline:Nn \l__exsheets_class_prop { \use:c {__exsheets_solutions_use_##1:} \bool_if:nTF { \l__exsheets_solutions_use_bool && ( !\bool_if_p:c {g__exsheets_use_##1_bool} || \bool_if_p:c {l__exsheets_##2_active_bool} ) } { \tl_put_right:Nn \l__exsheets_use_solution_tl {y} } { \tl_put_right:Nn \l__exsheets_use_solution_tl {n} } } % verwende, wenn ein Ja dabei war: \tl_if_in:NnT \l__exsheets_use_solution_tl {y} { \bool_set_true:N \l__exsheets_solutions_use_bool } \bool_if:NT \l__exsheets_questions_deactivate_bool { \bool_set_false:N \l__exsheets_solutions_use_bool } \bool_if:NT \l__exsheets_solutions_use_bool { \bool_set_true:N \l__exsheets_inside_solution_bool \IfNoValueF {#1} { \keys_set:nn {exsheets/solution} {#1} } } \tl_if_blank:VT \l__exsheets_solution_heading_instance_tl { \tl_set_eq:NN \l__exsheets_solution_heading_instance_tl \l__exsheets_heading_instance_tl } \__exsheets_solution_internal: } { \end__exsheets_solution_internal: } \cs_new_protected:Npn \__exsheets_solutions_name:nnn #1#2#3 { \tl_if_blank:VT \l__exsheets_solution_heading_instance_tl { \tl_set_eq:NN \l__exsheets_solution_heading_instance_tl \l__exsheets_heading_instance_tl } \exsheets_use_heading:Vnnnnn \l__exsheets_solution_heading_instance_tl {#2} {#1} { 0 } { 0 } {#3} } \cs_generate_variant:Nn \__exsheets_solutions_name:nnn { VV , VVV } \cs_new:Npn \exsheets_solutions_name_style:n #1 { \l_exsheets_solutions_name_style_tl #1 } % ---------------------------------------------------------------------------- % print solutions: % save section / chapter counter: \cs_new_protected:Npn \__exsheets_exlabel:n #1 { \@bsphack \exsheets_write_to_aux_x:n { \token_to_str:N \newlabel { exse:#1 } { { \arabic {section} } { \thepage } } } \cs_if_exist:NT \thechapter { \exsheets_write_to_aux_x:n { \token_to_str:N \newlabel { exch:#1 } { { \arabic {chapter} } { \thepage } } } } \@esphack } % user command: \cs_new_eq:NN \exlabel \__exsheets_exlabel:n % adapt \label, maybe do this _after_ BeginDocument: \bool_if:NT \l__exsheets_solutions_by_ref_bool { \cs_new_eq:NN \__exsheets_saved_label:n \label \cs_set:Npn \label #1 { \__exsheets_saved_label:n {#1} \__exsheets_exlabel:n {#1} } } % this is like \ref from latex.ltx \cs_new:Npn \__exsheets_exref:n #1 { \cs_if_exist:cTF { r@#1 } { \__exsheets_exref_aux:n {#1} } { 1\relax \protect\G@refundefinedtrue \@latex@warning{exlabel `#1' on page \thepage \space undefined}% } } \cs_new:Npn \__exsheets_exref_aux:n #1 { \exp_after:wN \exp_after:wN \exp_after:wN \use_i:nn \cs:w r@#1 \cs_end: } \cs_new_eq:NN \exref \__exsheets_exref:n % user command: \NewDocumentCommand \printsolutions { O{all} } { \exsheets_print_solutions:n {#1} } % internal command: \cs_new_protected:Npn \exsheets_print_solutions:n #1 { \group_begin: \bool_set_true:N \l__exsheets_solutions_print_bool \bool_set_true:N \l__exsheets_inside_solution_bool \cs_set:Npn \S ##1 { \exref { exse:##1 } } \cs_set:Npn \C ##1 { \exref { exch:##1 } } \keys_set:nn { exsheets / exsheets_print_solutions } {#1} \group_end: } \keys_define:nn {exsheets} { exsheets_print_solutions .choice: , exsheets_print_solutions / section .code:n = { \exsheets_print_solutions_section:n {#1} } , exsheets_print_solutions / chapter .code:n = { \exsheets_print_solutions_chapter:n {#1} } , exsheets_print_solutions / all .code:n = { \exsheets_print_solutions_all: } , exsheets_print_solutions / byID .code:n = { \exsheets_print_solutions_byID:n {#1} } } \cs_new_protected:Npn \__exsheets_get_sectioning_numbers: { \cs_if_exist:NT \chapter { \int_set:Nn \l__exsheets_counter_ch_int { \arabic {chapter} } } \int_set:Nn \l__exsheets_counter_sec_int { \arabic {section} } \int_set:Nn \l_exsheets_counter_qu_int { \arabic {question} } } % #1: numeric ID % #2: condition \cs_new_protected:Npn \__exsheets_print_solution_if:nn #1#2 { \bool_if:nT { #2 && \prop_if_in_p:Nn \g__exsheets_solutions_questions_id_prop {#1} } { \prop_get:NnN \g__exsheets_solutions_names_prop {#1} \l__exsheets_tmpa_tl \prop_get:NnN \g__exsheets_solutions_content_prop {#1} \l__exsheets_tmpb_tl \prop_get:NnN \g__exsheets_solutions_questions_id_prop {#1} \l__exsheets_tmpc_tl \prop_get:NnN \g__exsheets_solutions_counter_prop {#1} \l__exsheets_tmpd_tl \group_begin: \tl_set_eq:NN \CurrentQuestionID \l__exsheets_tmpc_tl \__exsheets_sectioning_hook:nV {ch} \l__exsheets_new_chapter_hook_tl \__exsheets_sectioning_hook:nV {sec} \l__exsheets_new_section_hook_tl \group_end: \__exsheets_print_solution:VVV \l__exsheets_tmpc_tl \l__exsheets_tmpa_tl \l__exsheets_tmpb_tl } } % print by section: \cs_new_protected:Npn \exsheets_print_solutions_section:n #1 { \tl_if_blank:nTF {#1} { \prop_map_inline:Nn \g_exsheets_question_identification_prop { \__exsheets_print_solutions_section:nnn {##1} {##2} { \arabic{section} } } } { \clist_map_inline:nn {#1} { \__exsheets_print_solutions_by_section:w ##1-- \q_stop } } } \cs_new_protected:Npn \__exsheets_print_solutions_by_section:w #1-#2-#3 \q_stop { % wenn #3=-, dann range-Input \tl_if_eq:nnTF {#3} { - } { \int_zero:N \l__exsheets_tmpa_int \int_set:Nn \l__exsheets_tmpb_int { 10000 } \tl_if_blank:nF {#1} { \int_set:Nn \l__exsheets_tmpa_int {#1} } \tl_if_blank:nF {#2} { \int_set:Nn \l__exsheets_tmpb_int {#2} } \int_do_while:nn { \l__exsheets_tmpa_int <= \l__exsheets_tmpb_int } { \prop_map_inline:Nn \g_exsheets_question_identification_prop { \__exsheets_print_solutions_section:nnn {##1} {##2} { \l__exsheets_tmpa_int } } \int_incr:N \l__exsheets_tmpa_int } } { \prop_map_inline:Nn \g_exsheets_question_identification_prop { \__exsheets_print_solutions_section:nnn {##1} {##2} {#1} } } } \cs_new_protected:Npn \__exsheets_print_solutions_section:nnn #1#2#3 { \__exsheets_read_number:w #2 \q_stop \__exsheets_print_solution_if:nn {#1} { \int_compare_p:n { \l__exsheets_counter_sec_int = #3 } } } % print by chapter: \cs_new_protected:Npn \exsheets_print_solutions_chapter:n #1 { \tl_if_blank:nTF {#1} { \prop_map_inline:Nn \g_exsheets_question_identification_prop { \__exsheets_print_solutions_chapter:nnn {##1} {##2} { \arabic{chapter} } } } { \clist_map_inline:nn {#1} { \__exsheets_print_solutions_by_chapter:w ##1-- \q_stop } } } \cs_new_protected:Npn \__exsheets_print_solutions_by_chapter:w #1-#2-#3 \q_stop { % wenn #3=-, dann range-Input \tl_if_eq:nnTF {#3} { - } { \int_zero:N \l__exsheets_tmpa_int \int_set:Nn \l__exsheets_tmpb_int { 10000 } \tl_if_blank:nF {#1} { \int_set:Nn \l__exsheets_tmpa_int {#1} } \tl_if_blank:nF {#2} { \int_set:Nn \l__exsheets_tmpb_int {#2} } \int_do_while:nn { \l__exsheets_tmpa_int <= \l__exsheets_tmpb_int } { \prop_map_inline:Nn \g_exsheets_question_identification_prop { \__exsheets_print_solutions_chapter:nnn {##1} {##2} { \l__exsheets_tmpa_int } } \int_incr:N \l__exsheets_tmpa_int } } { \prop_map_inline:Nn \g_exsheets_question_identification_prop { \__exsheets_print_solutions_chapter:nnn {##1} {##2} {#1} } } } \cs_new_protected:Npn \__exsheets_print_solutions_chapter:nnn #1#2#3 { \__exsheets_read_number:w #2 \q_stop \__exsheets_print_solution_if:nn {#1} { \int_compare_p:n { \l__exsheets_counter_ch_int = #3 } } } % print all: \cs_new_protected:Npn \exsheets_print_solutions_all: { \prop_map_function:NN \g_exsheets_question_identification_prop \__exsheets_print_solutions_all:nn % \prop_show:N \g_exsheets_question_identification_prop } \cs_new:Npn \__exsheets_sectioning_hook:nn #1#2 { \bool_if:nT { !\int_compare_p:n { \use:c { l__exsheets_current_#1_int } = \use:c { l__exsheets_counter_#1_int } } % && % !\int_compare_p:n % { \use:c { l__exsheets_counter_#1_int } = 0 } } {#2} } \cs_generate_variant:Nn \__exsheets_sectioning_hook:nn { nV } \cs_new_protected:Npn \__exsheets_print_solutions_all:nn #1#2 { \exsheets_print_solutions_if:nnn { \c_true_bool } {#1} {#2} } % #1: condition % #2: numeric question ID % #3: question number string (stored in \g_exsheets_question_identification_prop) % use function in a wrapper which in turn is fed to % \prop_map_function:NN \g_exsheets_question_identification_prop \cs_new_protected:Npn \exsheets_print_solutions_if:nnn #1#2#3 { \prop_if_in:NnT \g__exsheets_solutions_names_prop {#2} { \int_set_eq:NN \l__exsheets_current_sec_int \l__exsheets_counter_sec_int \int_set_eq:NN \l__exsheets_current_ch_int \l__exsheets_counter_ch_int \__exsheets_read_number:w #3 \q_stop \__exsheets_print_solution_if:nn {#2} {#1} } } \cs_generate_variant:Nn \exsheets_print_solutions_if:nnn {nnV} % print by ID: \seq_new:N \l__exsheets_solutions_byID_seq \cs_new_protected:Npn \exsheets_print_solutions_byID:n #1 { \seq_set_split:Nnn \l__exsheets_solutions_byID_seq { , } {#1} \seq_clear:N \l__exsheets_tmpa_seq \seq_map_inline:Nn \l__exsheets_solutions_byID_seq { \prop_get:NnN \g__exsheets_questions_id_prop {##1} \l__exsheets_tmpa_tl \seq_put_right:NV \l__exsheets_tmpa_seq \l__exsheets_tmpa_tl } \bool_if:NT \l__exsheets_print_byID_sorted_bool { \seq_sort:Nn \l__exsheets_tmpa_seq { \int_compare:nNnTF {##1} > {##2} { \sort_return_swapped: } { \sort_return_same: } } } \seq_map_inline:Nn \l__exsheets_tmpa_seq { \prop_get:NnNT \g_exsheets_question_identification_prop {##1} \l__exsheets_tmpa_tl { \exsheets_print_solutions_if:nnV { \c_true_bool } {##1} \l__exsheets_tmpa_tl } } } \cs_new_protected:Npn \__exsheets_read_number:w #1-#2-#3 \q_stop { \int_zero:N \l__exsheets_counter_ch_int \int_zero:N \l__exsheets_counter_sec_int \int_zero:N \l_exsheets_counter_qu_int \tl_if_in:nnF {#1} { @@@ } { \tl_if_blank:nF {#1} { \int_set:Nn \l__exsheets_counter_ch_int {#1} } } \int_set:Nn \l__exsheets_counter_sec_int {#2} \int_set:Nn \l_exsheets_counter_qu_int {#3} } \cs_new_protected:Npn \exsheets_solutions_print_name:nnn #1#2#3 { \tl_set_rescan:Nnn \l__exsheets_tmpa_tl { \char_set_catcode_letter:N \@ } {#1} \tl_if_in:nnTF {#1} { @@@ } { \__exsheets_solutions_name:nnn { } {#2} {#3} } { \__exsheets_solutions_name:nnn {#1} {#2} {#3} } \tex_penalty:D 10000 \scan_stop: } \cs_generate_variant:Nn \exsheets_solutions_print_name:nnn { V , VVV } \cs_new:Npn \__exsheets_surround_with:nnn #1#2#3 { #2#1#3 } \cs_generate_variant:Nn \__exsheets_surround_with:nnn { nVV } % think about the interface with \exsheetsprintsolution a bit more, especially % about the placement of the hooks! % #1: ID % #2: name % #3: body \cs_new_protected:Npn \__exsheets_print_solution:nnn #1#2#3 { \group_begin: \tl_set:Nn \CurrentQuestionID {#1} \tl_set:Nx \l__exsheets_tmpa_tl { \exsheets_question_number:n {#1} } \__exsheets_surround_with:nVV { \exp_args:Nnx \exsheetsprintsolution { \exsheets_solutions_print_name:Vnn \l__exsheets_tmpa_tl {#2} {#1} } { \exp_not:V \l__exsheets_solutions_pre_body_hook_tl \exp_not:n {#3} \exp_not:V \l__exsheets_solutions_post_body_hook_tl } } \l__exsheets_solutions_pre_hook_tl \l__exsheets_solutions_post_hook_tl \exsheets_add_space:N \l__exsheets_solutions_skip_below_dim \group_end: } \cs_generate_variant:Nn \__exsheets_print_solution:nnn { VV , VVV } % a user command to be redefined as needed; must have two mandatory arguments! % #1: heading % #2: body \cs_new:Npn \exsheetsprintsolution #1#2 { #1#2 } \cs_new:Npn \PrintSolutionsTF { \bool_if:NTF \l__exsheets_solutions_print_bool } \cs_new:Npn \PrintSolutionsT { \bool_if:NT \l__exsheets_solutions_print_bool } \cs_new:Npn \PrintSolutionsF { \bool_if:NF \l__exsheets_solutions_print_bool } \keys_define:nn { exsheets / solution } { headings .tl_set:N = \l__exsheets_solution_heading_instance_tl , print .choice: , print / true .code:n = { \bool_set_true:N \l__exsheets_solutions_print_bool \bool_set_false:N \l__exsheets_solutions_print_section_bool \bool_set_false:N \l__exsheets_solutions_print_chapter_bool \bool_set_false:N \l__exsheets_solutions_print_all_bool } , print / false .code:n = { \bool_set_false:N \l__exsheets_solutions_print_bool } , print .default:n = true , name .tl_set:N = \l__exsheets_solutions_name_tl , sorted .bool_set:N = \l__exsheets_print_byID_sorted_bool , skip-below .dim_set:N = \l__exsheets_solutions_skip_below_dim , no-skip-below .bool_set:N = \l__exsheets_no_skip_after_bool , pre-body-hook .tl_set:N = \l__exsheets_solutions_pre_body_hook_tl , post-body-hook .tl_set:N = \l__exsheets_solutions_post_body_hook_tl , pre-hook .tl_set:N = \l__exsheets_solutions_pre_hook_tl , post-hook .tl_set:N = \l__exsheets_solutions_post_hook_tl } % ---------------------------------------------------------------------------- % create question/solution pairs \cs_new_protected:Npn \__exsheets_new_qu_sol_pair:nnnnnn #1#2#3#4#5#6 { \NewDocumentEnvironment {#1} { O{}G{} } { \keys_set:nn {exsheets} {#3} \__exsheets_question: {#2,##1} {##2} } { \end__exsheets_question: } \NewDocumentEnvironment {#4} { O{} } { \keys_set:nn {exsheets} {#6} \__exsheets_solution: {#5,##1} } { \end__exsheets_solution: } } \cs_new_protected:Npn \__exsheets_renew_qu_sol_pair:nnnnnn #1#2#3#4#5#6 { \RenewDocumentEnvironment {#1} { O{}G{} } { \keys_set:nn {exsheets} {#3} \__exsheets_question: {#2,##1} {##2} } { \end__exsheets_question: } \RenewDocumentEnvironment {#4} { O{} } { \keys_set:nn {exsheets} {#6} \__exsheets_solution: {#5,##1} } { \end__exsheets_solution: } } \NewDocumentCommand \NewQuSolPair { mO{}O{}mO{}O{} } { \__exsheets_new_qu_sol_pair:nnnnnn {#1} {#2} {#3} {#4} {#5} {#6} } \@onlypreamble \NewQuSolPair \NewDocumentCommand \RenewQuSolPair { mO{}O{}mO{}O{} } { \__exsheets_renew_qu_sol_pair:nnnnnn {#1} {#2} {#3} {#4} {#5} {#6} } \@onlypreamble \RenewQuSolPair % ---------------------------------------------------------------------------- % the `tasks' list \RequirePackage {tasks} [2014/07/03] \AddCounterPattern* [tasks] {question} {qu} \ReadCounterFrom [tasks] {question} \l_exsheets_counter_qu_int % ---------------------------------------------------------------------------- % the \blank{} command \RequirePackage {ulem} \normalem \cs_new:Npn \exsheets_write_blank:n #1 {#1} \cs_set_eq:NN \exsheets_write_blank:n \uline \keys_define:nn { exsheets / blank } { style .choice: , style / line .code:n = \cs_set_eq:NN \exsheets_write_blank:n \uline , style / wave .code:n = \cs_set_eq:NN \exsheets_write_blank:n \uwave , style / dline .code:n = \cs_set_eq:NN \exsheets_write_blank:n \uuline , style / dotted .code:n = \cs_set_eq:NN \exsheets_write_blank:n \dotuline , style / dashed .code:n = \cs_set_eq:NN \exsheets_write_blank:n \dashuline , scale .tl_set:N = \l__exsheets_blank_scale_tl , width .code:n = { \bool_set_true:N \l__exsheets_blank_width_bool \dim_set:Nn \l__exsheets_blank_dim {#1} } , linespread .code:n = \bool_set_true:N \l__exsheets_blank_linespread_bool \tl_set:Nn \l__exsheets_blank_linespread_tl {#1} , line-increment .dim_set:N = \l__exsheets_blank_line_increment_dim , line-increment .initial:n = 1pt , line-minimum-length .dim_set:N = \l__exsheets_blank_line_minimum_length_dim , line-minimum-length .initial:n = 2em } \NewDocumentCommand \blank { som } { \group_begin: \IfNoValueF {#2} { \keys_set:nn { exsheets / blank } {#2} } \mode_if_vertical:TF { \IfBooleanF {#1} { \noindent } \exsheets_blank:n {#3} \IfBooleanF {#1} { \bool_if:NT \l__exsheets_blank_linespread_bool { \linespread { \l__exsheets_blank_linespread_tl } \selectfont } \par } } { \exsheets_blank:n {#3} } \group_end: } \cs_new_protected:Npn \exsheets_blank:n #1 { \box_clear:N \l__exsheets_blank_box \mode_if_math:TF { \hbox_set:Nn \l__exsheets_blank_box { $ \m@th \mathpalette{}{#1} $ } } { \hbox_set:Nn \l__exsheets_blank_box {#1} } \bool_if:NTF \l__exsheets_inside_solution_bool { \exsheets_write_blank:n {#1} } { \bool_if:NTF \l__exsheets_blank_width_bool { \__exsheets_blank_skip:V \l__exsheets_blank_dim } { \__exsheets_blank_skip:n { \box_wd:N \l__exsheets_blank_box } } } } \cs_new_protected:Npn \__exsheets_blank_skip:n #1 { \bool_if:NTF \l__exsheets_blank_width_bool { \dim_set:Nn \l__exsheets_tmpa_dim {#1} } { \fp_set:Nn \l__exsheets_tmpa_fp { \dim_to_fp:n {#1} * \l__exsheets_blank_scale_tl } \dim_set:Nn \l__exsheets_tmpa_dim { \fp_to_dim:N \l__exsheets_tmpa_fp } } \dim_compare:nTF { \l__exsheets_tmpa_dim > \l__exsheets_blank_line_minimum_length_dim } { \mode_if_math:TF { \exsheets_write_blank:n { \skip_horizontal:N \l__exsheets_tmpa_dim } } { \dim_do_while:nn { \l__exsheets_tmpa_dim > \c_zero_dim } { \tex_penalty:D \hyphenpenalty \dim_compare:nTF { \l__exsheets_tmpa_dim < \l__exsheets_blank_line_increment_dim } { \exsheets_write_blank:n { \skip_horizontal:N \l__exsheets_tmpa_dim } } { \exsheets_write_blank:n { \skip_horizontal:N \l__exsheets_blank_line_increment_dim } } \dim_sub:Nn \l__exsheets_tmpa_dim { \l__exsheets_blank_line_increment_dim } } } } { \exsheets_write_blank:n { \skip_horizontal:N \l__exsheets_tmpa_dim } } } \cs_generate_variant:Nn \__exsheets_blank_skip:n { V } % 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 % ---------------------------------------------------------------------------- % \examspace % insert space for a student to answer a question \cs_new_protected:Npn \__exsheets_examspace:nn #1#2 { \par \tex_penalty:D -100 \scan_stop: \dim_set:Nn \l__exsheets_tmpa_dim {#2} \dim_set:Nn \l__exsheets_tmpb_dim { \pagegoal - \pagetotal - \baselineskip } \dim_compare:nTF { \l__exsheets_tmpa_dim > \l__exsheets_tmpb_dim } { \dim_compare:nT { \l__exsheets_tmpb_dim > 0pt } { \vfil } \break \IfBooleanF {#1} { \dim_sub:Nn \l__exsheets_tmpa_dim { \l__exsheets_tmpb_dim } \vspace* { \l__exsheets_tmpa_dim } } } { \skip_vertical:N \l__exsheets_tmpa_dim } } \NewDocumentCommand \examspace { sm } { \__exsheets_examspace:nn {#1} {#2} } % ---------------------------------------------------------------------------- % SETUP \NewDocumentCommand \SetupExSheets { o +m } { \IfNoValueTF {#1} { \keys_set:nn {exsheets} {#2} } { \keys_set:nn { exsheets / #1 } {#2} } } % ---------------------------------------------------------------------------- % default definitions: % question / solution pair: \NewQuSolPair {question} {solution} % properties: % \DeclareQuestionProperty{number} \DeclareQuestionProperty {counter} \DeclareQuestionProperty {subtitle} \DeclareQuestionProperty {question-body} \DeclareQuestionProperty {points} \DeclareQuestionProperty {bonus-points} \bool_if:NT \l__exsheets_auto_label_bool { \DeclareQuestionProperty {ref} \DeclareQuestionProperty {pageref} } % classes: \DeclareQuestionClass {class} {classes} \DeclareQuestionClass {topic} {topics} % ---------------------------------------------------------------------------- % Sprachanpassungen \RequirePackage {translations} % translation for the exercises \DeclareTranslationFallback {exsheets-exercise-name} {Exercise} \DeclareTranslation {English} {exsheets-exercise-name} {Exercise} \DeclareTranslation {British} {exsheets-exercise-name} {Exercise} \DeclareTranslation {American} {exsheets-exercise-name} {Exercise} \DeclareTranslation {French} {exsheets-exercise-name} {Exercice} \DeclareTranslation {German} {exsheets-exercise-name} {\"Ubung} \DeclareTranslation {Italian} {exsheets-exercise-name} {Esercizio} \DeclareTranslation {Spanish} {exsheets-exercise-name} {Ejercicio} \DeclareTranslation {Catalan} {exsheets-exercise-name} {Exercici} \DeclareTranslation {Turkish} {exsheets-exercise-name} {Egzersiz} \DeclareTranslation {Croatian} {exsheets-exercise-name} {Primjer} \DeclareTranslation {Hungarian} {exsheets-exercise-name} {Gyakorol} \DeclareTranslation {Danish} {exsheets-exercise-name} {Opgave} \DeclareTranslation {Norsk} {exsheets-exercise-name} {Oppgave} \DeclareTranslation {Portuges} {exsheets-exercise-name} {Exerc\'\i cio} % translation for the question \DeclareTranslationFallback {exsheets-question-name} {Question} \DeclareTranslation {English} {exsheets-question-name} {Question} \DeclareTranslation {British} {exsheets-question-name} {Question} \DeclareTranslation {American} {exsheets-question-name} {Question} \DeclareTranslation {French} {exsheets-question-name} {Question} \DeclareTranslation {German} {exsheets-question-name} {Aufgabe} \DeclareTranslation {Italian} {exsheets-question-name} {Questione} \DeclareTranslation {Spanish} {exsheets-question-name} {Pregunta} \DeclareTranslation {Catalan} {exsheets-question-name} {Q\"uesti\'o} \DeclareTranslation {Turkish} {exsheets-question-name} {Soru} \DeclareTranslation {Croatian} {exsheets-question-name} {Zadatak} \DeclareTranslation {Hungarian} {exsheets-question-name} {Feladat} \DeclareTranslation {Danish} {exsheets-question-name} {Opgave} \DeclareTranslation {Norsk} {exsheets-question-name} {Oppgave} \DeclareTranslation {Portuges} {exsheets-question-name} {Quest\~ao} % translation for the solutions \DeclareTranslationFallback {exsheets-solution-name} {Solution} \DeclareTranslation {English} {exsheets-solution-name} {Solution} \DeclareTranslation {British} {exsheets-solution-name} {Solution} \DeclareTranslation {American} {exsheets-solution-name} {Solution} \DeclareTranslation {French} {exsheets-solution-name} {Solution} \DeclareTranslation {German} {exsheets-solution-name} {L\"osung} \DeclareTranslation {Italian} {exsheets-solution-name} {Soluzione} \DeclareTranslation {Spanish} {exsheets-solution-name} {Soluci\'on} \DeclareTranslation {Catalan} {exsheets-solution-name} {Soluci\'o} \DeclareTranslation {Turkish} {exsheets-solution-name} {\c C\"oz\"um} \DeclareTranslation {Croatian} {exsheets-solution-name} {Rje\v{s}enje} \DeclareTranslation {Hungarian} {exsheets-solution-name} {Megold\'{a}s} \DeclareTranslation {Danish} {exsheets-solution-name} {L\o sning} \DeclareTranslation {Norsk} {exsheets-solution-name} {L\o sning} \DeclareTranslation {Portuges} {exsheets-solution-name} {Solu\c c\~ao} % the actual translating \tl_set:Nn \l__exsheets_exercise_name_tl { \GetTranslation {exsheets-exercise-name} } \tl_set:Nn \l__exsheets_questions_name_tl { \GetTranslation {exsheets-question-name} } \tl_set:Nn \l__exsheets_solutions_name_tl { \GetTranslation {exsheets-solution-name} } % ---------------------------------------------------------------------------- % save total points in .aux file to make it available for \allpoints anywhere \AtEndDocument { \exsheets_write_to_aux_x:n { \exp_not:N \exsheets@sum@of@points { \fp_eval:n { \g__exsheets_points_sum_fp } }^^J \exp_not:N \exsheets@sum@of@bonus { \fp_eval:n { \g__exsheets_bonus_sum_fp } }^^J \exp_not:N \gdef \exp_not:N \numberofquestions { \int_use:N \g__exsheets_questions_used_int } % \exsheets@number@of@questions % { \int_to_arabic:V \g__exsheets_questions_id_int } } } % ---------------------------------------------------------------------------- % load custom configuration \file_if_exist:nT {exsheets_configurations.cfg} { \AtBeginDocument { \msg_info:nn {exsheets} {loading-configurations} \file_input:n {exsheets_configurations.cfg} } } \file_input_stop: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% HISTORY: 2011/11/23 v0.1pre - first upload to bitbucket and all changes until considered stable enough/ready to be called 0.1alpha. There'll be no further descriptions until then. 2012/06/08 v0.1alpha - first hopefully stable version 2012/06/11 v0.1alpha-a - added headings instances, slightly rewritten `exercises-tasks' object and `tasks' environment 2012/06/11 v0.1alpha-b - added `multiplechoice' instance and `load-tasks' option 2012/06/12 v0.1alpha-c - renamed from `exercises' into `ExSheets' 2012/06/15 v0.1alpha-d - added more flexible solution printing method 2012/06/15 v0.1beta - filled in some blanks in the documentation, now we need some testing! 2012/09/07 v0.1beta-a - adjustments to the updated l3kernel 2012/09/08 v0.1beta-b - sort solutions printed with the `byID' key 2012/09/16 v0.1beta-c - renamed option `questions-counter-format' into `counter-format' 2012/09/19 v0.1beta-d - added `topic' key, provide \includequestions with options `all' and `IDs' 2012/09/24 v0.1beta-e - added question properties and improved random question selection 2012/09/27 v0.1 - added two headings instances, new option `headings-format' - small changes in the definition of the `exsheets-headings' object - preliminary version of totoc options. Unfortunatly \exsheets@recover@number is not expandable => no hyperref support 2012/09/30 v0.2beta - version numbering gone crazy! this will now stay 0.2beta until upload to CTAN (v0.2) - integrated packages `translations' and `cntformats': trnslt: provide expandable version of `translator's \translate cntfmts: outsourced previous counter formatting commands; should now be usable in other packages, too, and simplified definitions in `ExSheets' - thanks to `translations' and `cntformats' the `totoc' option now works with `hyperref' 2012/10/05 v0.2 - removed \sumpoints and moved the functionality to \totalpoints* - changed meaning of \addpoints* and moved former functionality to \points - added bonus point functionality including \addbonus, \bonus, \bonussum, \pointssum, \currentpointssum, \currentbonussum - added grades distribution 2012/10/05 v0.2a - resolved bug in saving and recovering question number using \exsheets@save@number and \QuestionNumber 2012/10/08 v0.2b - improved the reference to chapter/solution numbers in \printsolutions - resolved bug in \printsolutions introduced in the last update - upload to CTAN 2012/10/23 v0.3 - bugfix in \exref - new: \NewQuSolPair, \RenewQuSolPair, \examspace 2012/10/24 v0.3a - fix of headings when followed by a list - changed syntax of {tasks} list to resemble the real lists like enumerate 2012/10/26 v0.3b - small fix to the {tasks} environment, \NewTasks, \RenewTasks, resolve stupid bug: \ckeckedchoicebox => \checkedchoicebox 2012/11/08 v0.4 - compatibility with KOMA-Script's `parskip' option: now no unwanted skip and no overfull hbox is produced - env {task} now supports inner environments 2012/12/02 v0.5 - added \CurrentQuestionID 2012/12/06 v0.5a - bug fix: solutions had wrong numbers with counter-format=se.qu and [print] option 2012/12/18 v0.6 - variations, Hungarian translations, corrected Catalanian translations 2012/12/23 v0.6a - bug fix: labels and items of {tasks} weren't joined at their baselines 2013/01/06 v0.6b - bug fix: ID counting fixed in \includequestions 2013/01/19 v0.7 - extracted `tasks' environment into standalone package; this also lead to a slightly new syntax and a few new options for it - changed internals for random selection of questions: uses `pgf' instead of `lcg' and seems to be more reliable than before 2013/01/21 v0.7a - added option `auto-label' and questions key `label', also added question properties `ref' and `pageref' 2013/02/17 v0.7b - bug fix: question selection by ID now works again 2013/03/10 v0.8 - \prop_length => \exsheets_prop_count - define unexpandable internal commands protected - translations basic dictionaries for English, German, French and Spanish - new \includequestions option `exclude' - new way of handling the `class' of questions: instead of hard-coded `classes' and `topics' individual groups like e.g. `difficulties' can be chosen; `classes' and `topics' are still provided for backwards compatibility; this introduces the new command \DeclareQuestionClass - minor changes to the debugging information layout - bug fix in number saving mechanism - \includequestions[random=] now obeys class as done with \SetupExSheets{use-topics={foo,bar}} - new macro \questionsincludedlast 2013/04/04 v0.8a - added Portuguese translations 2013/04/07 v0.9 - protected internal commands where appropriate - added possibility to set general options with \NewQuSolPair and \RenewQuSolPair 2013/04/08 v0.9a - added headings instance `empty' 2013/04/18 v0.9b - fixed erroneous behaviour of \includequestions when used more than once - added hook \l__exsheets_heading_points_post_hook_tl 2013/04/21 v0.9c - bug fix: \ForEachQuestion seems to work correctly again 2013/04/25 v0.9d - bug fix: \includequestions works correctly when used multiple times together with ordinary instances of the {question} environment - bug fix: points/parse=false correctly disables parsing points again - bug fix: \addpoints didn't add to the question property - new option `points/format' - \blank now works in math mode, it doesn't do linebreaks there if `ulem' doesn't allow them (which it doesn't) 2013/05/01 v0.9e - corrected erroneous behaviour of \examspace 2013/05/26 v0.9f - fixed incorrectly placed links when the `totoc' option is enabled and questions/solutions are at the top of a new page because there wasn't enough place left at the page before 2013/05/30 v0.9g - obey \if@filesw 2013/06/28 v0.9h - cleaner internal use of \exsheets_set_question_properties:n 2013/07/17 v0.9i - made \g__exsheets_total_bonus_fp and \g__exsheets_total_points_fp public - removed `translations' from the bundle 2013/10/11 v0.10 - require `translations' like any other package - added `exsheets-listings' package - added Norwegian translations - changed horizontal spaces declared as `1ex' into `.3333em' in the declarations of the headings instances - added subtitles 2013/11/07 v0.10a - fix bug in loading headings of questions; when included from an external file sometimes the wrong ID has been passed to the headings 2013/11/20 v0.11 - \GetQuestionClass{} - \PrintQuestionClass(TF) 2013/12/02 v0.12 - fix issue with \CurrentQuestionID - new options `label-format', `label-cmd', `ref-cmd' and `pageref-cmd' for `auto-label' options that allow specification of labelling command and thus compatibility with packages like `cleveref' - write question properties to aux file so they can be retrieved before the corresponding question is printed - make \GetQuestionProperty expandable - new syntax feature in points argument: a leading bang prevents the points from being added to the sum of points - added possibility for https://bitbucket.org/cgnieder/exsheets/issue/15 - added \IfQuestionSubtitle(TF) - added `subtitle' property - dropped `color' option 2013/12/08 v0.12a - changed details in the printing mechanism of the points which seems to be more consistent - fix bug introduced in the last update: subtitles work again 2013/12/27 v0.12b - ensure that points are not parsed if a leading bang is inserted - fix bug in parsing the points 2014/05/11 v0.13 - new options: * chapter-hook * section-hook These options provide hooks to add code in the list of solutions (when printed with \printsolutions[all]) when solutions from a new section or chapter are printed 2014/05/11 v0.13a - bug fix: solutions get the counter-format that's active when the corresponding `solution' environment is placed in the source 2014/06/27 v0.14 - new command: \ExSheetsHeading - \__exsheets_use_heading:nnnnnn => \exsheets_use_heading:nnnnnn - new options `question/pre-hook' and `question/post-hook' - new pre-defined property `question-body' - new pre-defined property `bonus-points' - new pre-defined property `counter' - new option `question/save-to-aux' - \ForEachQuestion, \numberofquestions and \iflastquestion are now available before any questions have been typeset 2014/07/20 v0.15 - remove `tasks' from the bundle and into a package of its own - remove `cntformats' from the bundle and into a package of its own - drop options `load-headings' and `load-tasks' - new command \IfQuestionPropertyTF 2014/09/14 v0.16 - fix `subtitle-format' option - allow that the format given in `headings-format' and `subtitle-format' can end with a command needing an argument - add options `solution/pre-hook', `solution/post-hook', `solution/pre-body-hook' and `solution/post-body-hook' (this adds a possibility for issue #21) - add options `question/pre-body-hook' and `question/post-body-hook' - add internal interface for the printing of solutions ( \__exsheets_print_solution:nnnn ) - fix issue #22 - fix issue #23 - use only one version number for the whole bundle 2014/10/14 v0.17 - new option `use-saved-counter-format' 2015/02/09 v0.18 - added correct Danish translations - thanks to Jonas Nyrup - introduce \exsheetsprintsolution, see http://tex.stackexchange.com/q/227078/ for motivation - change test in \exsheets_h_or_vspace:N to something more reliable and meaningful - new option `no-skip-below' 2015/05/06 v0.18a - fix bug in points mechanism - singular points name is now only used for exactly 1 point - a few cosmetic changes to the points function definitions 2015/07/04 v0.19 - add http://tex.stackexchange.com/q/222814 - allow begin and end of pseudo-environments as hooks to the question an solution environments 2015/11/18 v0.20 - add tagging feature - new: \DeclareExSheetsHeadingContainer 2016/01/26 v0.21 - give error message when \includequestions tries to load a file which can't be found (issue) - \exsheets_print_solutions_if:nnn - \l__exsheets_counter_qu_int => \l_exsheets_counter_qu_int - \g__exsheets_question_number_prop => \g_exsheets_question_identification_prop - make question properties accessable when question is not printed 2016/01/26 v0.21a - remove deprecated \prop_get:Nn (=> \prop_item:Nn) 2016/02/01 v0.21b - fix annoying bug in \exsheets_print_solutions_if:nnn 2016/03/21 v0.21c - use question property `counter' for retrieving the counter patterns in solution's headings (this resolves http://tex.stackexchange.com/q/299898) - remove option `use-saved-counter-format': solutions should always have the same number pattern as the corresponding questions 2016/08/14 v0.21d - fix issue #32 2016/09/07 v0.21e - fix issue #29 - fix issue #35 - fix issue #36 2016/09/17 v0.21f - make \exsheets_question_number:n an alias of \exsheets_get_question_property:nn {counter}; this also makes \QuestionNumber an alias of \GetQuestionProperty {counter} 2016/10/25 v0.21g - smaller steps when creating the blank lines 2016/11/28 v0.21h - bug in \exsheets@used@id fixed 2017/02/08 v0.21i - adapt to l3sort integration into l3kernel 2019/09/27 v0.21j - fix bug #44 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % TODO: - solve the problem using verbatim material in questions and solutions (difficult) - allow for different kinds of problems/solutions using an independant counter (not trivial) - points/decimal-marker, points/frac (?), points/format (?,im interface) - points: swedish style - \examspace inside {tasks} => possible? (\pagegoal-\pagetotal) gives wrong values here - remove doubled points name if total points are unknown