% ---------------------------------------------------------------------------- % the EXSHEETS package % % Yet another package for the creation of exercise sheets % % ---------------------------------------------------------------------------- % Clemens Niederberger % Web: https://bitbucket.org/cgnieder/exsheets/ % E-Mail: contact@mychemistry.eu % ---------------------------------------------------------------------------- % Copyright 2011-2013 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. % ---------------------------------------------------------------------------- % The exsheets package consists of the files % - exsheets.sty, exsheets_headings.def, exsheets_headings.cfg, % exsheets_tasks.def, exsheets_tasks.cfg, exsheets_configurations.cfg, % - exsheets_en.tex, exsheets_en.pdf, % - README % ---------------------------------------------------------------------------- % If you have any ideas, questions, suggestions or bugs to report, please % feel free to contact me. % ---------------------------------------------------------------------------- \def\exsheets@date{2013/04/04} \def\exsheets@version{0.8a} \RequirePackage { expl3 , xparse } \ProvidesExplPackage {exsheets} {\exsheets@date} {\exsheets@version} {Yet another package for the creation of exercise sheets and exams.} % ---------------------------------------------------------------------------- % 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 \dim_new:N \l__exsheets_tmpa_dim \seq_new:N \l__exsheets_tmpa_seq \fp_new:N \l__exsheets_tmpa_fp \clist_new:N \l__exsheets_tmpa_clist % ---------------------------------------------------------------------------- % messages \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} { headings } { You~requested~the~headings~instance~`#1'~\msg_line_context: \c_space_tl which~is~not~defined.~Did~you~forget~to~use~the~package~option~ `load-headings'?~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.cgf'. } \msg_new:nnn { exsheets } { deprecated-command } { The~command~"~\token_to_str:N #1 \c_space_tl"~is~deprecated.~ Use~"~\token_to_str:N #2 \c_space_tl"~instead. } \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:Npn \exsheets_depr_cmd:nn #1#2 { \cs_new:Npn #1 { \msg_warning:nnnn { exsheets } { deprecated-command } { #1 } { #2 } #2 } } % ---------------------------------------------------------------------------- \RequirePackage { xtemplate , l3sort , l3keys2e , xcolor , etoolbox , environ , pgfcore } % ---------------------------------------------------------------------------- % color: \xdefinecolor { exsheetsblue } { rgb } { 0.02 , 0.04 , 0.48 } \xdefinecolor { exsheetsred } { rgb } { 0.65 , 0.04 , 0.07 } \tl_new:N \g__exsheets_color_tl \tl_gset:Nn \g__exsheets_color_tl { exsheetsblue } % ---------------------------------------------------------------------------- % how are questions/solutions/... counted? \bool_new:N \l__exsheets_print_number_bool \bool_set_true:N \l__exsheets_print_number_bool \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_current_pattern_interpretation_tl \tl_new:N \l__exsheets_current_pattern_tl \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: \bool_new:N \l__exsheets_load_headings_bool \bool_new:N \l__exsheets_load_tasks_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 \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_user_format_tl \tl_set:Nn \l__exsheets_heading_title_user_format_tl { \normalsize\bfseries } \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 } \AtBeginDocument{ \cs_if_exist:NF \phantomsection { \cs_new:Npn \phantomsection {} } } \keys_define:nn { exsheets } { color .tl_gset:N = \g__exsheets_color_tl , counter-format .code:n = \SaveCounterPattern [ exsheets ] \l__exsheets_qu_counter_pattern_tl \l__exsheets_qu_counter_interpretation_tl { #1 } , counter-within .code:n = \@addtoreset{question}{#1} , headings .tl_set:N = \l__exsheets_heading_instance_tl , load-headings .bool_set:N = \l__exsheets_load_headings_bool , headings-format .tl_set:N = \l__exsheets_heading_title_user_format_tl , load-tasks .bool_set:N = \l__exsheets_load_tasks_bool , 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 , auto-label .bool_set:N = \l__exsheets_auto_label_bool } \keys_set:nn { exsheets} { counter-format = qu. } % process package options: \ProcessKeysOptions { exsheets } % ---------------------------------------------------------------------------- % points for exercises \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 \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 \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 { (+ } \tl_new:N \l__exsheets_points_post_bonus_marker_tl \tl_set:Nn \l__exsheets_points_post_bonus_marker_tl { ) } \cs_new_nopar:Npn \exsheets_set_points_name:n #1 { \tl_if_in:nnTF { #1 } { / } { \__exsheets_set_points_name_aux:nn #1 \q_stop } { \__exsheets_set_points_name_aux:nn #1 / \q_stop } } \cs_new_nopar:Npn \__exsheets_set_points_name_aux:nn #1/#2 \q_stop { \tl_set:Nn \l__exsheets_points_name_tl { #1 } \tl_set:Nn \l__exsheets_points_name_plural_tl { #1#2 } } \cs_new_nopar:Npn \exsheets_set_bonus_name:n #1 { \tl_if_in:nnTF { #1 } { / } { \__exsheets_set_bonus_name_aux:nn #1 \q_stop } { \__exsheets_set_bonus_name_aux:nn #1 / \q_stop } } \cs_new_nopar:Npn \__exsheets_set_bonus_name_aux:nn #1/#2 \q_stop { \tl_set:Nn \l__exsheets_bonus_name_tl { #1 } \tl_set:Nn \l__exsheets_bonus_name_plural_tl { #1#2 } } % FIXME: \fp_add will deprecate! \cs_new_nopar:Npn \exsheets_add_points:n #1 { \fp_gadd:Nn \g__exsheets_points_sum_fp { #1 } } \cs_generate_variant:Nn \exsheets_add_points:n { V } \cs_new_nopar:Npn \exsheets_add_bonus:n #1 { \fp_gadd:Nn \g__exsheets_bonus_sum_fp { #1 } } \cs_generate_variant:Nn \exsheets_add_bonus:n { V } \cs_new_nopar:Npn \exsheets@sum@of@points #1 { \fp_gset:Nn \g__exsheets_total_points_fp { #1 } } \cs_new_nopar:Npn \exsheets@sum@of@bonus #1 { \fp_gset:Nn \g__exsheets_total_bonus_fp { #1 } } \cs_new_nopar:Npn \exsheets_parse_points:n #1 { \group_begin: \tl_use:N \l__exsheets_points_number_format_tl { \bool_if:NTF \l__exsheets_parse_points_bool { \exsheets_num:n { \fp_to_decimal:n { #1 } } } { \use:n { \fp_to_decimal:n { #1 } } } } \group_end: } \cs_generate_variant:Nn \exsheets_parse_points:n { V } \cs_new_nopar:Npn \exsheets_parse_bonus:n #1 { \group_begin: \tl_use:N \l__exsheets_bonus_number_format_tl { \bool_if:NTF \l__exsheets_parse_points_bool { \exsheets_num:n { \fp_to_decimal:n { #1 } } } { \use:n { \fp_to_decimal:n { #1 } } } } \group_end: } \cs_generate_variant:Nn \exsheets_parse_bonus:n { V } \cs_new_nopar:Npn \exsheets_num:n #1 { \group_begin: \fp_set:Nn \l__exsheets_tmpa_tl { #1 } \fp_to_tl:N \l__exsheets_tmpa_tl \group_end: } \cs_generate_variant:Nn \exsheets_num:n { x } \cs_new_nopar:Npn \exsheets_points_name:n #1 { \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 \l__exsheets_points_name_plural_tl } { \fp_compare:nTF { #1 <= 1 } { \tl_use:N \l__exsheets_points_name_tl } { \tl_use:N \l__exsheets_points_name_plural_tl } } } { \tl_use:N \l__exsheets_points_name_plural_tl } } } } \cs_generate_variant:Nn \exsheets_points_name:n { V } \cs_new_nopar:Npn \exsheets_bonus_name:n #1 { \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 \l__exsheets_bonus_name_plural_tl } { \fp_compare:nTF { #1 <= 1 } { \tl_use:N \l__exsheets_bonus_name_tl } { \tl_use:N \l__exsheets_bonus_name_plural_tl } } } { \tl_use:N \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_nopar:Npn \exsheets_print_points:n #1 { \exsheets_parse_points:n { #1 } \exsheets_points_name:n { #1 } } \cs_generate_variant:Nn \exsheets_print_points:n { V } \cs_new_nopar: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 } { \IfBooleanTF { #1 } { \exsheets_parse_points:n { \g__exsheets_total_points_fp } } { \exsheets_print_points:n { \g__exsheets_total_points_fp } } } \NewDocumentCommand \bonussum { s } { \IfBooleanTF { #1 } { \exsheets_parse_bonus:n { \g__exsheets_total_bonus_fp } } { \exsheets_print_bonus:n { \g__exsheets_total_bonus_fp } } } \NewDocumentCommand \currentpointssum { s } { \IfBooleanTF { #1 } { \exsheets_parse_points:n { \g__exsheets_points_sum_fp } } { \exsheets_print_points:n { \g__exsheets_points_sum_fp } } } \NewDocumentCommand \currentbonussum { s } { \IfBooleanTF { #1 } { \exsheets_parse_bonus:n { \g__exsheets_bonus_sum_fp } } { \exsheets_print_bonus:n { \g__exsheets_bonus_sum_fp } } } \NewDocumentCommand \totalpoints { s } { \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 } } } } } \exsheets_depr_cmd:nn { \sumpoints } { \totalpoints* } \cs_new_nopar: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_nopar:Npn \exsheets_totalbonus: { \bool_if:NT \l__exsheets_parse_points_bool { \fp_compare:nF { \g__exsheets_total_bonus_fp = 0 } { \tl_use:N \c_space_tl \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 , 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 \int_zero_new:N \l__exsheets_variations_int \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: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 \prop_new:N \l__exsheets_relgrades_prop \fp_new:N \l__exsheets_grade_round_fp \fp_zero:N \l__exsheets_grade_round_fp \bool_new:N \l__exsheets_grades_half_bool \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: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 \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_solutions_use_bool \bool_set_true:N \l__exsheets_solutions_use_bool \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 } \cs_new_nopar:Npn \exsheets_glue:N #1 { \cleaders \vbox:n {} \skip_vertical:N #1 } \keys_define:nn { exsheets } { skip-below .code:n = \dim_set:Nn \l__exsheets_questions_skip_below_dim { #1 } \dim_set:Nn \l__exsheets_solutions_skip_below_dim { #1 } } % Einteilung in Klassen, Themen und andere Gruppierungen: \prop_new:N \l__exsheets_class_prop \tl_new:N \l__exsheets_step_counter_tl \bool_new:N \l__exsheets_use_this_question_bool \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 } % \exsheets_set_question_properties:n { #1 = ##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:cff { g__exsheets_questions_#1_prop } { \int_to_arabic:n { \g__exsheets_questions_id_int } } { \tl_use:c { l__exsheets_questions_#1_tl } } % add to #2 list, if it's a new one \prop_get:cfN { g__exsheets_#2_prop } { \tl_use:c { l__exsheets_questions_#1_tl } } \l__exsheets_tmpa_tl \tl_if_eq:NNT \l__exsheets_tmpa_tl \q_no_value { \prop_gput:cfn { g__exsheets_#2_prop } { \tl_use:c { 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:cfN { g__exsheets_#2_active_prop } { \tl_use:c { l__exsheets_questions_#1_tl } } \l__exsheets_tmpa_tl \tl_if_eq:NNTF \l__exsheets_tmpa_tl \q_no_value { \bool_set_false:c { l__exsheets_#1_active_bool } \bool_set_false:N \l__exsheets_questions_print_bool } { \bool_set_true:c { l__exsheets_#1_active_bool } \bool_set_true:N \l__exsheets_use_this_question_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:NoN \g__exsheets_questions_id_prop { \g__exsheets_questions_current_id_tl } \l__exsheets_tmpa_tl % get question #1 from number \prop_get:coN { g__exsheets_questions_#1_prop } { \l__exsheets_tmpa_tl } \l__exsheets_tmpb_tl % test if #1 is active \prop_get:coN { g__exsheets_#2_active_prop } { \l__exsheets_tmpb_tl } \l__exsheets_tmpc_tl \tl_if_eq:NNT \l__exsheets_tmpc_tl \q_no_value { \bool_set_false:N \l__exsheets_solutions_use_bool } } } } \cs_generate_variant:Nn \prop_get:NnN { cf } \cs_generate_variant:Nn \prop_gput:Nnn { cf , cff } \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 } % \exsheets_declare_question_property:n { #1 } } \@onlypreamble \DeclareQuestionClass % ---------------------------------------------------------------------------- % die 'question' Umgebung \tl_new:N \l__exsheets_questions_name_tl \tl_set:Nn \l__exsheets_questions_name_tl { Question } \tl_new:N \l__exsheets_exsheets_name_tl \tl_set:Nn \l__exsheets_exsheets_name_tl { Exercise } \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 \int_new:N \g__exsheets_questions_id_int \int_gzero:N \g__exsheets_questions_id_int \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 \NewEnviron { __exsheets_questions_internal: } { \csappto { BODY } { \cs_if_eq:NNTF \exsheets_par: \scan_stop: { \c_space_tl } { \exsheets_par: \dim_compare:nNnT { \parskip } = { 0pt } { \exsheets_glue:N \l__exsheets_questions_skip_below_dim } } } \bool_if:nT { \l__exsheets_questions_print_bool && \g__exsheets_questions_use_bool && !\l__exsheets_questions_deactivate_bool } { \__exsheets_save_number_in:N \g__exsheets_question_number_prop \BODY } } \prop_new:N \g__exsheets_questions_id_prop \prop_new:N \g__exsheets_questions_meta_prop \prop_new:N \g__exsheets_questions_used_prop \int_new:N \g__exsheets_questions_used_int \tl_new:N \l__exsheets_questions_id_tl \tl_new:N \l__exsheets_questions_meta_tl \tl_new:N \g__exsheets_questions_current_id_tl \tl_new:N \l__exsheets_title_tl \tl_new:N \l__exsheets_questions_label_tl \bool_new:N \l__exsheets_points_not_given_bool \cs_new_protected:Npn \__exsheets_read_points: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:Npn \__exsheets_read_points:n #1 { \__exsheets_read_points:w #1++ \q_stop } \NewDocumentEnvironment { __exsheets_question: } { gg } { \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: \IfNoValueF { #1 } { \keys_set:nn { exsheets / question } { #1 } } % ID: \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 { \prop_gput:NVV \g__exsheets_questions_id_prop \g__exsheets_questions_id_int \g__exsheets_questions_id_int \bool_if:NT \l__exsheets_questions_use_bool { \prop_gput:NVV \g__exsheets_questions_used_prop \g__exsheets_questions_id_int \g__exsheets_questions_id_int \int_gincr:N \g__exsheets_questions_used_int } \tl_gset:Nx \g__exsheets_questions_current_id_tl { \int_use:N \g__exsheets_questions_id_int } } { \prop_gput:NVV \g__exsheets_questions_id_prop \l__exsheets_questions_id_tl \g__exsheets_questions_id_int \bool_if:NT \l__exsheets_questions_use_bool { \prop_gput:NVV \g__exsheets_questions_used_prop \l__exsheets_questions_id_tl \g__exsheets_questions_id_int \int_gincr:N \g__exsheets_questions_used_int } \tl_gset:Nx \g__exsheets_questions_current_id_tl { \tl_use:N \l__exsheets_questions_id_tl } } \tl_if_blank:VTF \l__exsheets_questions_label_tl { \bool_if:NT \l__exsheets_auto_label_bool { \exp_args:Nx \label { qu : \g__exsheets_questions_current_id_tl } \exp_args:Nx \SetQuestionProperties { ref = \exp_not:N \ref { qu : \g__exsheets_questions_current_id_tl } , pageref = \exp_not:N \pageref { qu : \g__exsheets_questions_current_id_tl } } } } { \exp_args:Nx \label { \l__exsheets_questions_label_tl } \exp_args:Nx \SetQuestionProperties { ref = \exp_not:N \ref { \l__exsheets_questions_label_tl } , pageref = \exp_not:N \pageref { \l__exsheets_questions_label_tl } } } } % Punktevergabe: \IfNoValueTF { #2 } { \__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 \exp_args:Nx \SetQuestionProperties { points = \fp_to_tl:N \l__exsheets_points_default_fp } } } { \__exsheets_read_points:n { #2 } \SetQuestionProperties{points=#2} } \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 } % meta: \tl_if_blank:VF \l__exsheets_questions_meta_tl { \prop_gput:NVV \g__exsheets_questions_meta_prop \g__exsheets_questions_current_id_tl \l__exsheets_questions_meta_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: } } \bool_if:nT { \g__exsheets_questions_use_bool && !\l__exsheets_questions_deactivate_bool } { \tl_clear:N \l__exsheets_step_counter_tl % das ist etwas mühsam: abhängig von der Nutzung der verschiedenen % Gruppierungsmöglichkeiten muss der Counter erhöht werden: \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_put_right:Nn \l__exsheets_step_counter_tl { y } } { \tl_put_right:Nn \l__exsheets_step_counter_tl { n } } } % erhöhe jetzt, wenn kein Nein dabei war: \tl_if_in:NnF \l__exsheets_step_counter_tl { n } { \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_add_points:V \l__exsheets_question_points_fp \exsheets_add_bonus:V \l__exsheets_question_bonus_fp } \bool_if:NT \l__exsheets_questions_print_bool { \bool_if:NTF \l__exsheets_exam_bool { \tl_set_eq:NN \l__exsheets_title_tl \l__exsheets_questions_name_tl } { \tl_set_eq:NN \l__exsheets_title_tl \l__exsheets_exsheets_name_tl } \__exsheets_use_heading:VVVnn \l__exsheets_question_heading_instance_tl \l__exsheets_title_tl \l__exsheets_qu_counter_interpretation_tl { \l__exsheets_question_points_fp } { \l__exsheets_question_bonus_fp } } } \__exsheets_questions_internal: } { \end__exsheets_questions_internal: } % current question number: \prop_new:N \g__exsheets_question_number_prop \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 } \iow_now:Nx \@auxout { \exsheets@save@number { #2 } { #3 } } } \cs_generate_variant:Nn \prop_get:NnNTF { Nx } \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} } } } } \cs_new:Npn \__exsheets_restore_number_from_and_do:Nnn #1#2#3 { \prop_get:NnN #1 { #2 } \l__exsheets_tmpc_tl \tl_if_eq:NNF \l__exsheets_tmpc_tl \q_no_value { \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 } \NewDocumentCommand \QuestionNumber { m } { \exsheets_question_number:n { #1 } } \cs_new_protected:Npn \exsheets_question_number:n #1 { \prop_get:NnNTF \g__exsheets_questions_id_prop { #1 } \l__exsheets_tmpa_tl { \__exsheets_question_number:c { exsheets@question@\l__exsheets_tmpa_tl } } { \textbf{??} } } \cs_new: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 } } % ---------------------------------------------------------------------------- % title of a question: \input { exsheets_headings.def } \bool_if:NT \l__exsheets_load_headings_bool { \input { exsheets_headings.cfg } } % #1: instance % #2: title % #3: number % #4: points % #5: bonus \cs_new_nopar:Npn \__exsheets_use_heading:nnnnn #1#2#3#4#5 { \IfInstanceExistTF { exsheets-heading } { #1 } { \UseInstance { exsheets-heading } { #1 } } { \msg_warning:nnx { exsheets} { headings } { #1 } \UseInstance { exsheets-heading } { block } } { #2 } { #3 } { #4 } { #5 } } \cs_generate_variant:Nn \__exsheets_use_heading:nnnnn { V , VV , VVV } \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 % 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_exsheets_name_tl { #1 } \tl_set:Nn \l__exsheets_questions_name_tl { #1 } , headings .tl_set:N = \l__exsheets_question_heading_instance_tl , meta .tl_set:N = \l__exsheets_questions_meta_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 } % ---------------------------------------------------------------------------- % 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 } \NewDocumentCommand \SetQuestionProperties { m } { \exsheets_set_question_properties:n { #1 } } \cs_new_protected:Npn \__exsheets_question_set_property:nn #1#2 { \prop_gput:cVn { g__exsheets_question_property_#1_prop } \g__exsheets_questions_current_id_tl { #2 } } \NewDocumentCommand \GetQuestionProperty { mm } { \exsheets_get_question_property:nn { #1 } { #2 } } \cs_new:Npn \exsheets_get_question_property:nn #1#2 { \prop_if_in:cnT { g__exsheets_question_property_#1_prop } { #2 } { \prop_get:cn { g__exsheets_question_property_#1_prop } { #2 } } } \int_new:N \g__exsheets_tmpa_int \cs_new_protected:Npn \ForEachQuestion #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 } } \cs_new:Npn \iflastquestion { \int_compare:nTF { \g__exsheets_questions_used_int = \g__exsheets_tmpa_int } } \cs_new:Npn \numberofquestions { \int_use:N \g__exsheets_questions_used_int } % ---------------------------------------------------------------------------- % debug-Info: \bool_new:N \l__exsheets_questions_debug_bool \bool_set_false:N \l__exsheets_questions_debug_bool \colorlet { exsheetsdebugcolor } { yellow } \cs_new:Npn \exsheets_debug_box:nn #1#2 { \exsheets_par: \noindent \group_begin: \normalfont\normalsize\normalcolor \colorbox { exsheetsdebugcolor } { \parbox { \dim_eval:n { #1 - 2 \fboxsep } } { #2 } } \group_end: \exsheets_par: } \cs_new:Npn \__exsheets_list_comma: {} \cs_new_nopar:Npn \exsheets_questions_debug: { \bool_if:nT { \l__exsheets_questions_debug_bool && \l__exsheets_inside_question_bool } { \exsheets_debug_box:nn { \linewidth } { \fbox { ID:~ \textit { \tl_if_blank:VTF \l__exsheets_questions_id_tl { \int_use:N \g__exsheets_questions_id_int } { \tl_use:N \l__exsheets_questions_id_tl } } } \cs_set_protected:Npn \__exsheets_list_comma: { ~ \cs_set:Npn \__exsheets_list_comma: { ,~ } } \prop_map_inline:Nn \l__exsheets_class_prop { \bool_if:cT { l__exsheets_questions_##2_bool } { \__exsheets_list_comma: ##2:~ \textit { \tl_use:c { l__exsheets_questions_##2_tl } } } } } } } \NewDocumentCommand \DebugExSheets { G{true} } { \use:c { bool_set_#1:N } \l__exsheets_questions_debug_bool } \cs_new:Npn \CurrentQuestionID { \g__exsheets_questions_current_id_tl } % ---------------------------------------------------------------------------- % include random/selected questions from a file: \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_set_true:N \l__exsheets_include_all_bool \prop_new:N \g__exsheets_selected_questions_prop \clist_new:N \l__exsheets_include_id_clist \clist_new:N \l__exsheets_exclude_id_clist \clist_new:N \questionsincludedlast \int_new:N \l__exsheets_include_random_int \int_zero:N \l__exsheets_include_random_int \cs_new:Npn \exsheets_file_input_if_exist:n #1 { \file_if_exist:nT { #1 } { \file_input:n { #1 } } } \cs_new_protected:Npn \exsheets_prop_count:N #1 { \__exsheets_prop_count:NN #1 \l_tmpa_int \int_use:N \l_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 \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_tmpc_int \g__exsheets_questions_id_int \exsheets_file_input_if_exist:n { ##1 } \__exsheets_prop_count:NN \g__exsheets_selected_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_selected_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_selected_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 \__exsheets_select_question_random:n { ##1 } \clist_gclear:N \questionsincludedlast \bool_if:nTF { \l__exsheets_include_random_bool || \l__exsheets_include_by_id_bool } { \clist_gset_eq:NN \questionsincludedlast \l__exsheets_include_id_clist } { \prop_map_inline:Nn \g__exsheets_selected_questions_prop { \clist_if_in:NnF \l__exsheets_exclude_id_clist { ####2 } { \clist_gput_right:Nn \questionsincludedlast { ####2 } } } } \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_selected_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 } \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 following option does _not_ set \l__exsheets_include_random_bool % so it can be combined with the `random' option: exclude .code:n = \bool_set_false:N \l__exsheets_include_all_bool \bool_set_false:N \l__exsheets_include_by_id_bool \clist_set:Nn \l__exsheets_exclude_id_clist { #1 } , 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 } } \int_new:N \g__exsheets_selection_number_int \cs_new_protected:Npn \__exsheets_select_question:n #1 { \bool_if:NT \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 \exp_args:NNx \prop_gput:Nnn \g__exsheets_selected_questions_prop { \int_use:N \g__exsheets_selection_number_int } { #1 } } } { \int_gincr:N \g__exsheets_questions_id_int \int_set:Nn \l__exsheets_tmpa_int { \int_mod:nn { \g__exsheets_questions_id_int } { \g__exsheets_selection_number_int } } \int_compare:nT { \l__exsheets_tmpa_int = 0 } { \int_set_eq:NN \l__exsheets_tmpa_int \g__exsheets_selection_number_int } \prop_get:NxN \g__exsheets_selected_questions_prop { \int_use:N \l__exsheets_tmpa_int } \l__exsheets_tmpa_tl \bool_if:NF \l__exsheets_include_all_bool { \__exsheets_select_question_by_id:V \l__exsheets_tmpa_tl } } } } \cs_generate_variant:Nn \__exsheets_select_question:n { V } \cs_generate_variant:Nn \prop_get:NnN { Nx } \cs_new_protected:Npn \__exsheets_select_question_by_id:n #1 { \bool_if:nTF { \l__exsheets_include_by_id_bool || \l__exsheets_include_random_bool } {% parsing list of included IDs \clist_if_in:NnTF \l__exsheets_include_id_clist { #1 } { \bool_gset_true:N \g__exsheets_questions_use_bool } { \bool_gset_false:N \g__exsheets_questions_use_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 } } } \cs_generate_variant:Nn \__exsheets_select_question_by_id:n { V } \tl_new:N \l__exsheets_include_id_tl \bool_new:N \l__exsheets_use_selection_bool % 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_selected_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_selected_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_selected_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 } } } } } \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 } } } } } } } } \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: \bool_new:N \l__exsheets_solutions_print_bool \bool_set_false:N \l__exsheets_solutions_print_bool \bool_new:N \l__exsheets_solutions_print_here_bool \bool_set_false:N \l__exsheets_solutions_print_here_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 % \prop_new:N \g__exsheets_question_number_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 \tl_new:N \l__exsheets_solutions_name_tl \tl_set:Nn \l__exsheets_solutions_name_tl { Solution } \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:Nff \g__exsheets_solutions_content_prop { \int_use:N \g__exsheets_questions_id_int } { #1 } \prop_gput:Nff \g__exsheets_solutions_questions_id_prop { \int_use:N \g__exsheets_questions_id_int } { \tl_use:N \g__exsheets_questions_current_id_tl } \prop_gput:Nff \g__exsheets_solutions_names_prop { \int_use:N \g__exsheets_questions_id_int } { \tl_use:N \l__exsheets_solutions_name_tl } % print here \bool_if:NT \l__exsheets_solutions_print_here_bool { \int_set:Nn \l__exsheets_counter_qu_int { \arabic{question} } \__exsheets_get_sectioning_numbers: \__exsheets_solutions_name:VV \l__exsheets_qu_counter_interpretation_tl \l__exsheets_solutions_name_tl \use:n { #1 } \cs_if_eq:NNTF \exsheets_par: \scan_stop: { \c_space_tl } { \exsheets_par: \dim_compare:nNnT { \parskip } = { 0pt } { \exsheets_glue:N \l__exsheets_solutions_skip_below_dim } } } } \cs_generate_variant:Nn \prop_gput:Nnn { Nff,Nfx,Nfn,Nxx } \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 } } \bool_new:N \l__exsheets_inside_solution_bool \bool_set_false:N \l__exsheets_inside_solution_bool \tl_new:N \l__exsheets_use_solution_tl \NewDocumentEnvironment { __exsheets_solution: } { g } { \tl_clear:N \l__exsheets_use_solution_tl \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 { l__exsheets_##2_active_bool } ) || ! \bool_if_p:c { g__exsheets_use_##1_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: } \bool_new:N \l__exsheets_solutions_runin_bool \bool_set_false:N \l__exsheets_solutions_runin_bool \cs_new_nopar:Npn \__exsheets_solutions_name:nn #1#2 { \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:Vnnnn \l__exsheets_solution_heading_instance_tl { #2 } { #1 } { 0 } { 0 } } \cs_generate_variant:Nn \__exsheets_solutions_name:nn { VV } \tl_new:N \l__exsheets_solutions_name_style_tl \tl_set:Nn \l__exsheets_solutions_name_style_tl { \normalsize \bfseries } \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 \iow_now:Nx \@auxout { \token_to_str:N \newlabel { exse:#1 } { { \arabic { section } } { \thepage } } } \cs_if_exist:NT \thechapter { \iow_now:Nx \@auxout { \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_ref:n #1 { \cs_if_exist:cTF { r@#1 } { \__exsheets_ref_aux:n { #1 } } { 1\relax \protect\G@refundefinedtrue \@latex@warning{exlabel `#1' on page \thepage \space undefined}% } } \cs_new:Npn \__exsheets_ref_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_ref: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 } } } \int_new:N \l__exsheets_counter_ch_int \int_new:N \l__exsheets_counter_sec_int \int_new:N \l__exsheets_counter_qu_int \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} } } \cs_new:Npn \__exsheets_print_solution_if:nn #1#2 { \exp_args:No \int_compare:nT { #2 } { \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 \eSaveCounterPatternFrom [ exsheets ] \l__exsheets_current_pattern_interpretation_tl \l__exsheets_current_pattern_tl \l__exsheets_qu_counter_pattern_tl \exsheets_solutions_print_name:VV \l__exsheets_current_pattern_tl \l__exsheets_tmpa_tl \tl_use:N \l__exsheets_tmpb_tl \cs_if_eq:NNTF \exsheets_par: \scan_stop: { \c_space_tl } { \exsheets_par: \dim_compare:nNnT { \parskip } = { 0pt } { \exsheets_glue:N \l__exsheets_solutions_skip_below_dim } } } } % print by section: \cs_new:Npn \exsheets_print_solutions_section:n #1 { \tl_if_blank:nTF { #1 } { \prop_map_inline:Nn \g__exsheets_question_number_prop { \prop_if_in:NnT \g__exsheets_solutions_names_prop { ##1 } { \__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_eq:NN \l__exsheets_tmpb_int \c_ten_thousand \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_number_prop { \prop_if_in:NnT \g__exsheets_solutions_names_prop { ##1 } { \__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_number_prop { \prop_if_in:NnT \g__exsheets_solutions_names_prop { ##1 } { \__exsheets_print_solutions_section:nnn { ##1 } { ##2 } { #1 } } } } } \cs_new:Npn \__exsheets_print_solutions_section:nnn #1#2#3 { \__exsheets_read_number:w #2 \q_stop \__exsheets_print_solution_if:nn { #1 } { \l__exsheets_counter_sec_int = #3 } } % print by chapter: \cs_new:Npn \exsheets_print_solutions_chapter:n #1 { \tl_if_blank:nTF { #1 } { \prop_map_inline:Nn \g__exsheets_question_number_prop { \prop_if_in:NnT \g__exsheets_solutions_names_prop { ##1 } { \__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_eq:NN \l__exsheets_tmpb_int \c_ten_thousand \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_number_prop { \prop_if_in:NnT \g__exsheets_solutions_names_prop { ##1 } { \__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_number_prop { \prop_if_in:NnT \g__exsheets_solutions_names_prop { ##1 } { \__exsheets_print_solutions_chapter:nnn { ##1 } { ##2 } { #1 } } } } } \cs_new:Npn \__exsheets_print_solutions_chapter:nnn #1#2#3 { \__exsheets_read_number:w #2 \q_stop \__exsheets_print_solution_if:nn { #1 } { \l__exsheets_counter_ch_int = #3 } } % print all: \cs_new:Npn \exsheets_print_solutions_all: { \prop_map_function:NN \g__exsheets_question_number_prop \__exsheets_print_solutions_all:nn } \cs_new_protected:Npn \__exsheets_print_solutions_all:nn #1#2 { \__exsheets_read_number:w #2 \q_stop \prop_get:NnNT \g__exsheets_solutions_names_prop { #1 } \l__exsheets_tmpa_tl { \tl_set_eq:NN \l__exsheets_solutions_name_tl \l__exsheets_tmpa_tl \prop_get:NnN \g__exsheets_solutions_content_prop { #1 } \l__exsheets_tmpb_tl \eSaveCounterPatternFrom [ exsheets ] \l__exsheets_current_pattern_interpretation_tl \l__exsheets_current_pattern_tl \l__exsheets_qu_counter_pattern_tl \exsheets_solutions_print_name:VV \l__exsheets_current_pattern_tl \l__exsheets_tmpa_tl \tl_use:N \l__exsheets_tmpb_tl \cs_if_eq:NNTF \exsheets_par: \scan_stop: { \c_space_tl } { \exsheets_par: \dim_compare:nNnT { \parskip } = { 0pt } { \exsheets_glue:N \l__exsheets_solutions_skip_below_dim } } } } % 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_reversed: } { \sort_ordered: } } } \seq_map_inline:Nn \l__exsheets_tmpa_seq { \group_begin: \tl_if_eq:nnF { ##1 } { \q_no_value } { \prop_get:NnNT \g__exsheets_solutions_names_prop { ##1 } \l__exsheets_tmpa_tl { \prop_get:NnN \g__exsheets_solutions_content_prop { ##1 } \l__exsheets_tmpb_tl \__exsheets_restore_number_from_and_do:Nnn \g__exsheets_question_number_prop { ##1 } { \eSaveCounterPatternFrom [ exsheets ] \l__exsheets_current_pattern_interpretation_tl \l__exsheets_current_pattern_tl \l__exsheets_qu_counter_pattern_tl \exsheets_solutions_print_name:VV \l__exsheets_current_pattern_tl \l__exsheets_tmpa_tl \tl_use:N \l__exsheets_tmpb_tl \cs_if_eq:NNTF \exsheets_par: \scan_stop: { \c_space_tl } { \exsheets_par: \dim_compare:nNnT { \parskip } = { 0pt } { \exsheets_glue:N \l__exsheets_solutions_skip_below_dim } } } } } \group_end: } } \cs_new_nopar: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_nopar:Npn \exsheets_solutions_print_name:nn #1#2 { \tl_set_rescan:Nnn \l__exsheets_tmpa_tl { \char_set_catcode_letter:N \@ } { #1 } \tl_if_in:nnTF { #1 } { @@@ } { \__exsheets_solutions_name:nn { } { #2 } } { \__exsheets_solutions_name:nn { #1 } { #2 } } \tex_penalty:D \c_ten_thousand } \cs_generate_variant:Nn \exsheets_solutions_print_name:nn { VV } \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 } \bool_new:N \l__exsheets_print_byID_sorted_bool \bool_set_true:N \l__exsheets_print_byID_sorted_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_true:N \l__exsheets_solutions_print_here_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 } % ---------------------------------------------------------------------------- % create question/solution pairs \cs_new:Npn \__exsheets_new_qu_sol_pair:nnnn #1#2#3#4 { \NewDocumentEnvironment { #1 } { O{}G{} } { \__exsheets_question: {#2,##1} {##2} } { \end__exsheets_question: } \NewDocumentEnvironment { #3 } { O{} } { \__exsheets_solution: {#4,##1} } { \end__exsheets_solution: } } \cs_new:Npn \__exsheets_renew_qu_sol_pair:nnnn #1#2#3#4 { \RenewDocumentEnvironment { #1 } { O{}G{} } { \__exsheets_question: {#2,##1} {##2} } { \end__exsheets_question: } \RenewDocumentEnvironment { #3 } { O{} } { \__exsheets_solution: {#4,##1} } { \end__exsheets_solution: } } \NewDocumentCommand \NewQuSolPair { mO{}mO{} } { \__exsheets_new_qu_sol_pair:nnnn { #1 } { #2 } { #3 } { #4 } } \@onlypreamble \NewQuSolPair \NewDocumentCommand \RenewQuSolPair { mO{}mO{} } { \__exsheets_renew_qu_sol_pair:nnnn { #1 } { #2 } { #3 } { #4 } } \@onlypreamble \RenewQuSolPair % ---------------------------------------------------------------------------- % the `tasks' list \bool_if:NTF \l__exsheets_load_tasks_bool { \RequirePackage [ more ] { tasks } } { \RequirePackage { tasks } } \AddCounterPattern* [ tasks ] { question } { qu } \ReadCounterFrom [ tasks ] { question } \l__exsheets_counter_qu_int % ---------------------------------------------------------------------------- % the \blank{} command \RequirePackage { ulem } \normalem \dim_new:N \l__exsheets_blank_dim \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 } \box_new:N \l__exsheets_blank_box \bool_new:N \l__exsheets_blank_width_bool \bool_new:N \l__exsheets_blank_linespread_bool \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 } } \NewDocumentCommand \blank { som } { \group_begin: \IfNoValueF { #2 } { \keys_set:nn { exsheets / blank } { #2 } } \mode_if_horizontal:TF { \exsheets_blank:n { #3 } } { \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 } } \group_end: } \cs_new_protected:Npn \exsheets_blank:n #1 { \box_clear:N \l__exsheets_blank_box \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:N \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_tmpa_fp { \dim_to_fp:n { #1 } } \fp_mul:Nn \l_tmpa_fp { \l__exsheets_blank_scale_tl } \dim_set:Nn \l__exsheets_tmpa_dim { \fp_to_dim:N \l_tmpa_fp } } \dim_compare:nTF { \l__exsheets_tmpa_dim > 2em } { \exsheets_write_blank:n { \skip_horizontal:n { 1em } } \dim_sub:Nn \l__exsheets_tmpa_dim { 2em } \dim_do_while:nn { \l__exsheets_tmpa_dim > \c_zero_dim } { \tex_penalty:D \hyphenpenalty \dim_compare:nTF { \l__exsheets_tmpa_dim < 1pt } { \exsheets_write_blank:n { \skip_horizontal:N \l__exsheets_tmpa_dim } } { \exsheets_write_blank:n { \skip_horizontal:n { 1pt } } } \dim_sub:Nn \l__exsheets_tmpa_dim { 1pt } } \exsheets_write_blank:n { \skip_horizontal:n { 1em } } } { \exsheets_write_blank:n { \skip_horizontal:N \l__exsheets_tmpa_dim } } } \cs_generate_variant:Nn \__exsheets_blank_skip:N { n } % the following code from Heiko Oberdieck in d.c.t.t served as inspiration % and basis for the \blank command: % % \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: \group_begin: \dim_set:Nn \l_tmpa_dim { #2 } \dim_set:Nn \l_tmpb_dim { \pagegoal - \pagetotal } \dim_compare:nTF { \l_tmpa_dim > \l_tmpb_dim } { \vfil \break \IfBooleanF { #1 } { \dim_sub:Nn \l_tmpa_dim { \l_tmpb_dim } \vspace* { \l_tmpa_dim } } } { \vspace* { #2 } } \group_end: } \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{points} \bool_if:NT \l__exsheets_auto_label_bool { \DeclareQuestionProperty{ref} \DeclareQuestionProperty{pageref} } % classs: \DeclareQuestionClass{class}{classes} \DeclareQuestionClass{topic}{topics} % ---------------------------------------------------------------------------- % Sprachanpassungen \file_if_exist:nT { translations.sty } { \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 } { Exercise } \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 } { \O{}velse } \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 { 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 } { Opl\o{}sning } \DeclareTranslation { Portuges } { exsheets-solution-name } { Solu\c c\~ao } % the actual translating \tl_set:Nn \l__exsheets_exsheets_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 { \iow_now:Nx \@auxout { \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 } } } } % ---------------------------------------------------------------------------- % load custom configuration \file_if_exist:nT { exsheets_configurations.cfg } { \AtBeginDocument { \msg_info:nn { exsheets } { loading-configurations } \file_input:n { exsheets_configurations.cfg } } } \tex_endinput:D %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 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 - `translations' got \LoadDictionary and \LoadDictionaryFor added and loads of languages defined. 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 - translations: new command \DeclareDictTranslation - 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 - translations: bug fix in \DeclareDictTranslation - exsheets: added Portuguese translations - tasks: corrected \seq_length:N => \seq_count:N %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % TODO: - points/decimal-marker, points/frac (?), points/format (?,im interface)