% Source (with comments) can be found at https://github.com/Strauman/exercisebank/ %% The LaTeX package exercisebank - version v0.2.0 (2018/07/21) - build 75 %% exercisebank.sty: Exercise bank %% ------------------------------------------------------------------------------------------- %% Copyright (c) 2018 by Andreas Storvik Strauman %% ------------------------------------------------------------------------------------------- %% This work may be distributed and/or modified under the %% conditions of the LaTeX Project Public License, either version 1.3c %% 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.3c or later is part of all distributions of LaTeX %% version 2008/05/04 or later. %% This work has the LPPL maintenance status `author-maintained'. %% This work consists of all files listed in README \ProvidesPackage{exercisebank}[2018/07/21 v0.2.0 Creates a setup that gives freedom in making sets and exercises.] \xdef\exbank@quiet{1} \PackageWarningNoLine{exercisebank-v0.2.0}{This package is in the beta stage, and if something unexpected happens please, oh pretty please, report it to https://github.com/Strauman/exercisebank. Thank you!} \@ifundefined{ownLineNoSpacesGotIt}{\@latex@warning{Compilation wont work unless \string\end{problem} and \string\end{solution} are on their own lines and without any spaces. This warning can be removed by doing \string\def\string\ownLineNoSpacesGotIt{} before \@backslashchar usepackage{exercisebank}} }{} \gdef\setExercisesDir#1{\@latex@error{\string\setExercisesDir is deprecated. Use \string\exercisebanksetup{exercise directory=#1} instead}{}\stop\bye } \@ifundefined{exercisesDir}{\gdef\@exercisesDir{.} }{} \global\let\incl = \input \usepackage{xstring} \usepackage{pgffor} \usepackage{scrextend} \usepackage{comment} \usepackage{calc} \usepackage{pgfpages} \usepackage{geometry} \usepackage{listofitems} \usepackage{trimspaces} \usepackage{needspace} \edef\@isTrue{1} \edef\@isFalse{0} \gdef\isin#1#2#3#4{ \def\needle{#2} \def\haystack{#1} \def\isFalse{0} \let\isInList = \isFalse \IfInteger{\needle}{}{\@latex@error{ISIN: Can't look for part problem that is not numeric!}{}\bye\stop} \IfInteger{\haystack}{ \ifnum\haystack=\needle\relax #3 \else #4 \fi }{ \foreach\pp in #1{ \ifnum\pp=\needle\relax% \gdef\isInList{1} #3 \fi } \if\isInList\isFalse #4 \fi } } \gdef\strif#1#2{% \ifnum\pdfstrcmp{#1}{#2}=\z@\relax% } \let\ea = \expandafter \begingroup\lccode`\|=`\\ \lowercase{\endgroup\def\removebs#1{\if#1|\else#1\fi}} \gdef\exbank@macroname#1{\expandafter\removebs\string#1} \gdef\@noneofone#1{} \gdef\@noneoftwo#1#2{} \@ifundefined{exbank@quiet}{ \global\let\@dlog\@latex@warning }{ \global\let\@dlog\@noneofone } \newcommand\ex@before[2]{\edef #1{\unexpanded{#2}\unexpanded\expandafter{#1}} } \newcommand{\exb@lmargin}[2]{\strut\vadjust{\exb@domark{#1}{#2}}} \newcommand{\exb@domark}[2]{% \hbox to #2{ \vbox to 0pt{ \kern-\dp\strutbox \smash{\llap{#1}} \vss }% } } \edef\@isTrue{1} \edef\@isFalse{0} \gdef\ShowNumbers{\gdef\@displayMetaCounter{\isTrue}} \gdef\@DisplaySolutions{\@isFalse} \gdef\DisplaySolutions{\xdef\@DisplaySolutions{\isTrue}\@latex@warning{Showing solutions}} \gdef\@SolutionsOnly{\@isFalse} \gdef\SolutionsOnly{\xdef\@SolutionsOnly{\@isTrue}\DisplaySolutions} \pgfkeys{/exbanksetup/.is family, /exbanksetup, default/.style = { part problems = On, tighten paragraphs = True, problem header={\normalsize\Large\bfseries\@tr{Problem}~\theproblemcounter}, part problem header={\large\textbf{(\theproblemcounter\alph{partproblemcounter})}}, solution header={\large{\textbf{\@tr{Solution}:}}}, exercise directory=., figure root directory=\@exercisesDir, style margins=On, problem needs space=0.2\paperheight }, exercise directory/.estore in = \@exercisesDir, % figure root directory/.estore in = \@figrootDir, % front page/.estore in = \exb@frontpage, front page=\@isFalse, problem header/.store in = \exbank@opt@problemHeader, part problem header/.store in = \exbank@opt@partProblemHeader, solution header/.store in = \exbank@opt@solutionHeader, problem needs space/.store in = \exbank@opt@problemneedspace, part problems/.style = {switches/#1/.get = \exbank@opt@partProblems}, tighten paragraphs/.style = {switches/#1/.get = \exbank@opt@tightenparagraphs}, style margins/.style={switches/#1/.get = \exbank@opt@doMargins}, switches/.cd, On/.initial = \@isTrue, on/.initial = \@isTrue, T/.initial = \@isTrue, True/.initial = \@isTrue, Off/.initial = \@isFalse, off/.initial = \@isFalse, F/.initial = \@isFalse, False/.initial = \@isFalse, } \newcommand\exercisebanksetup[1]{ \pgfkeys{/exbanksetup, #1}% } \exercisebanksetup{default} \AtBeginDocument{\xdef\tpd{\the\prevdepth}} \gdef\tighten@paragraph{% \if\exbank@opt@tightenparagraphs\@isTrue\relax% \filbreak% \fi% } \gdef\tighten@paragraph@solutions{% \if\exbank@opt@tightenparagraphs\@isTrue\relax% \if\@SolutionsOnly\@isFalse \if\@DisplaySolutions\@isTrue \filbreak% \fi \fi \fi% } \gdef\tighten@paragraph@always{% \if\exbank@opt@tightenparagraphs\@isTrue\relax% \filbreak% \fi% } \pgfkeys{ /exbanki18n/.is family, /exbanki18n, default/.style = {Problem = Problem, Solution = Solution}, Problem/.estore in = \@tr@Problem, Solution/.estore in = \@tr@Solution } \newcommand{\translateExBank}[1]{ \pgfkeys{/exbanki18n, default, #1}% } \translateExBank{} \gdef\@tr#1{% \@ifundefined{@tr@#1}{#1}{% \csname @tr@#1\endcsname% }% } \let\ea = \expandafter \begingroup\lccode`\|=`\\ \lowercase{\endgroup\def\removebs#1{\if#1|\else#1\fi}} \newcommand{\At}[2]{ \edef\mname{\exbank@macroname{#1}} \ifcsname At@\mname\endcsname \ea\g@addto@macro\csname At@\mname\endcsname{#2} \else \ea\gdef\csname At@\mname\endcsname{#2} \AtEndDocument{ \expandafter\let\csname At@\mname\endcsname\relax } \fi } \newcommand{\Trigger}[1]{ \edef\mname{\exbank@macroname{#1}} \ea\@ifundefined{At@\mname}{}{% \csname At@\mname\endcsname% }% } \xdef\figuresPath{\@figrootDir/} \gdef\fileInputPath{} \gdef\fileInputBase{} \providecommand*{\input@path}{} \g@addto@macro\input@path{{\fileInputBase}{\fileInputPath}} \At\PathControl{ \xdef\figuresPath{\@figrootDir/} \edef\exfile{\exerciseFile} \expandafter\filename@parse{\exfile} \xdef\fileInputPath{\figuresPath\filename@area} \xdef\fileInputBase{\figuresPath\filename@area\filename@base/} \graphicspath{{\fileInputBase}{\fileInputPath}} \@dlog{\fileInputBase} \@ifundefined{input@path}{\gdef\input@path{{\fileInputBase}{\fileInputPath}}}{} } \newlength{\pMarginBelow} \newlength{\pMarginAbove} \newlength{\pMarginLeft} \newlength{\ppMarginBelow} \newlength{\ppMarginAbove} \newlength{\ppMargin} \newlength{\introMargin} \newlength{\solutionMarginLeft} \newlength{\solutionMarginAbove} \edef\defaultLeftMargin{\the\dimexpr1in+\hoffset} \setlength{\ppMargin}{0.5em} \setlength{\pMarginLeft}{-\ppMargin-2.5em} \setlength{\solutionMarginLeft}{\ppMargin} \setlength{\solutionMarginAbove}{1em} \setlength{\pMarginBelow}{0em} \setlength{\pMarginAbove}{\baselineskip} \setlength{\introMargin}{-1.5em} \setlength{\ppMarginBelow}{\baselineskip} \setlength{\ppMarginAbove}{0em} \setlength\parindent{0pt} \newbox\ppmarkbox \newlength\markskip \setlength\markskip{4\baselineskip} \def\solMargin{\dimexpr\ppMargin\relax} \def\vsSize{1em} \def\vs{\vspace{\vsSize}} \newcounter{problemcounter} \setcounter{problemcounter}{0} \newcounter{partproblemcounter} \At\VeryBeginProblem{% \if\exbank@opt@tightenparagraphs\@isTrue\relax% \needspace{\exbank@opt@problemneedspace}% \vskip-2\baselineskip\relax% \fi% \stepcounter{problemcounter}% \setcounter{partproblemcounter}{0}% \vspace*{\pMarginAbove}% \strut\vadjust{\vbox to 0pt{\if\exbank@opt@doMargins\@isTrue\hskip\pMarginLeft\fi{\exbank@opt@problemHeader}\vss}}\par% \vspace*{\the\dimexpr\baselineskip+\pMarginBelow}% } \At\EndProblem{% \tighten@paragraph% } \At\VeryBeginPartproblem{% \stepcounter{partproblemcounter}% \bgroup% \if\@displayMetaCounter\@isTrue\relax% \ex@before\exbank@opt@partProblemHeader{{\Large\themetacounter}:}% \fi% \if\exbank@opt@doMargins\@isTrue\relax% \leavevmode\smash{\llap{{\exbank@opt@partProblemHeader}\hskip\ppMargin}}% \else{\exbank@opt@partProblemHeader}\fi% \ignorespaces}% \At\EndPartproblem{% \egroup% \vspace*{\pMarginBelow}% \tighten@paragraph\par% } \At\BeginSolution{\hfill\break\vspace*{\solutionMarginAbove}{\exbank@opt@solutionHeader}} \At\EndSolution{} \At\EndProblem{% \if\exbank@opt@tightenparagraphs\@isTrue\relax% \if\@SolutionsOnly\@isFalse% \if\@DisplaySolutions\@isTrue% \penalty-300% \fi% \fi% \fi% } \@ifundefined{figure}{}{\edef\figure{\unexpanded{\tighten@paragraph@always}\unexpanded\expandafter{\figure}} } \@ifundefined{endfigure}{}{\g@addto@macro\endfigure{\tighten@paragraph@always} } \gdef\isFalse{0} \gdef\isTrue{1} \gdef\DisplayProblem{\isTrue} \gdef\@displayMetaCounter{\isFalse} \AtBeginDocument{ \if\@DisplaySolutions\isFalse \@latex@warning{Hiding solutions. Show them with \string\DisplaySolutions} \fi } \global\let\do@ProcessCutFile = \ProcessCutFile \gdef\showhideproblem#1{% \if\DisplayProblem\isFalse \def\ProcessCutFile{} \else \if\@SolutionsOnly\@isTrue\relax% \def\ProcessCutFile{}\fi% #1 \fi } \generalcomment{problem}{ \edef\DisplaySolution{\@DisplaySolutions} \stepcounter{metacounter} \Trigger\DecideProblemDisplay \begingroup \showhideproblem{% \if\exbank@opt@partProblems\isTrue \Trigger\BeginPartproblem \Trigger\VeryBeginPartproblem \else \Trigger\BeginProblem \Trigger\VeryBeginProblem \fi } }{ \if\DisplayProblem\@isFalse\relax\else \if\exbank@opt@partProblems\isTrue \Trigger\EndPartproblem \tighten@paragraph \else \Trigger\EndProblem \fi \fi \endgroup } \generalcomment{solution} {\Trigger\AtBeginSolutionHard \begingroup \if\@DisplaySolutions\isTrue \if\DisplayProblem\isFalse \xdef\DisplaySolution{\isFalse} \fi \fi \if\DisplaySolution\isTrue \Trigger\BeginSolution \else \def\ProcessCutFile{} \fi }{\if\DisplaySolution\isTrue \Trigger\EndSolution \tighten@paragraph@solutions \vspace*{0.5em} \fi \endgroup \Trigger\EndSolutionHard% } \def\intromargin#1{% \if\exbank@opt@doMargins\@isTrue% \gdef\endintromargin{\endlist\endgroup}% \begingroup\list{}{\leftmargin#1}\item[]% \else% \relax\global\let\endintromargin=\relax\fi% } \generalcomment{intro}{ \if\@countIntros\isTrue \stepcounter{metacounter} \Trigger\DecideProblemDisplay \begingroup \if\@displayMetaCounter\isTrue\relax% {\exb@lmargin{{\Large\themetacounter}}{\pMarginLeft}} \fi \if\@spriteMode\@isTrue \if\introarg\@isTrue \stepcounter{partproblemcounter} \fi \fi \showhideproblem{% \tighten@paragraph% \Trigger\BeginIntro% } \fi% \intromargin{-1em} }{ \if\@countIntros\isTrue \Trigger\EndIntro \endgroup \fi\par\endintromargin } \def\squeeze{ \newlength{\marginw} \setlength{\marginw}{1.5cm} \newgeometry{margin=\marginw} \renewcommand{\topfraction}{0.85} \renewcommand{\bottomfraction}{0.85} \renewcommand{\textfraction}{0.1} \renewcommand{\floatpagefraction}{0.85} \renewcommand{\dbltopfraction}{0.85} \renewcommand{\dblfloatpagefraction}{.85} \setcounter{topnumber}{25} \setcounter{bottomnumber}{25} \setcounter{totalnumber}{25} \setcounter{dbltopnumber}{25} \renewcommand{\baselinestretch}{0.9} \let\markeverypar\everypar \newtoks\everypar \everypar\markeverypar \markeverypar{\the\everypar\looseness=-1\relax} \g@addto@macro\define@newfont{% \fontdimen2\font@name=0.8\fontdimen2\font@name }% \fontdimen2\font@name=0.8\fontdimen2\font@name \thinmuskip=0mu plus 3mu \medmuskip=1mu plus 4mu \thickmuskip=2mu plus 5mu \abovedisplayskip=0pt plus 3pt \belowdisplayskip=0pt plus 3pt \abovedisplayshortskip=0pt plus 3pt \belowdisplayshortskip=0pt plus 3pt } \let\ea = \expandafter \newcounter{metacounter} \setcounter{metacounter}{0} \gdef\emptyList{0} \gdef\ifppMode#1{ \def\mname{\exbank@macroname{#1}} \strif\mname\ppMode } \gdef\isppMode#1#2{ \edef\mname{\exbank@macroname{#1}} \strif{\exbank@macroname{#1}}{\ppMode}\relax#2\fi } \gdef\@ifppMode#1{% \ifnum\pdfstrcmp{\exbank@macroname{#1}}{\ppMode}=\z@% \expandafter\@firstofone% \else% \expandafter\@noneofone% \fi% }% \def\pm@Exclude{exclude} \def\pm@Select{select} \def\pm@Normal{normal} \let\ipm\@ifppMode \let\T@\isTrue \let\F@\isFalse \At\DecideProblemDisplay{% \ifnum\pdfstrcmp{\ppList}{\emptyList}=\z@\relax% \xdef\DisplayProblem{\ipm\exclude\T@\ipm\select\F@} \else \isin{\ppList}{\themetacounter}{ \xdef\DisplayProblem{\ipm\exclude\F@\ipm\select\T@\ipm\normal\T@} }{ \xdef\DisplayProblem{\ipm\exclude\T@\ipm\select\F@\ipm\normal\T@} } \fi } \pgfkeys{ /makeset/.is family, /makeset, default/.style = {noheadarg=\isFalse, introarg=\isFalse}, intro/.style = {introarg=\isTrue}, nohead/.style = {noheadarg=\isTrue}, introarg/.estore in = \introarg, noheadarg/.estore in = \noheadarg } \gdef\@listOfSets{} \gdef\@spriteMode{\isFalse} \gdef\exbank@spriteSets{\emptyList} \long\gdef\spritesets#1{ \gdef\exbank@spriteSets{#1} } \global\let\exlist@protect\noexpand \newcommand\makeset[3][]{ \xdef\thissetid{#2} \ea\xdef\csname setkeys@\thissetid\endcsname{#1} \pgfkeys{/makeset, default, #1}% \if\introarg\isTrue% \gdef\introarg{\isTrue} \ea\gdef\csname setlist@#2@intro\endcsname{\isTrue} \fi \if\noheadarg\isTrue% \ea\gdef\csname setlist@#2@nohead\endcsname{\isTrue} \fi \ea\gdef\csname setlist@#2\endcsname{\exlist@protect{#3}} \def\setmacro{\unexpanded\expandafter{\csname setlist@#2\endcsname}} \g@addto@macro\@listOfSets{,#2} } \long\gdef\about#1{} \newcommand\sprite[1][4]{\squeeze \gdef\@spriteMode{\isTrue} \long\def\about##1{{\Large\textbf{About}:\\[1.1em]##1\\[1.5em]}} \pgfpagesuselayout{#1 on 1}[a4paper,border shrink=5mm] \ifnum\pdfstrcmp{\exbank@spriteSets}{\emptyList}=\z@\relax% \edef\sprite@setlist{\ea\@secondoftwo\@listOfSets} \readlist*\list@sprite@setlist\sprite@setlist \foreachitem\set\in\list@sprite@setlist{ \edef\theset{{\set}} \if\theset\empty\relax\else% \buildset{\set}% \fi% } \else \edef\sprite@setlist{\exbank@spriteSets} \readlist*\list@sprite@setlist\sprite@setlist \foreachitem\set\in\list@sprite@setlist{ \edef\theset{{\set}} \if\theset\empty\relax\else% \buildex{\set}% \fi% } \fi } \gdef\exbank@setEnv@normal#1{\exbank@setEnv{#1}{normal} } \newcommand{\exbank@setEnv}[3][{-1}]{ \if\@spriteMode\isFalse% \gdef\ppList{#1} \gdef\exerciseFile{#2} \gdef\ppMode{#3} \else \gdef\ppList{\emptyList} \gdef\exerciseFile{#2} \gdef\ppMode{\pm@Normal} \fi } \newcommand{\exclude}[2]{[{#2}]{#1}{exclude}} \newcommand{\select}[2]{[{#2}]{#1}{select}} \gdef\buildex#1{ \makeset{#1}{#1} \buildset{#1} } \gdef\exbank@def@makeset@command#1#2{% \edef\macroname{\exbank@macroname{#1}}% \xdef#1{?{@\macroname}}% \ea\gdef\csname @\macroname\endcsname{#2}% } \let\exbank@isFirstProblem\isTrue \exbank@def@makeset@command{\phead}{ \if\exbank@isFirstProblem\isTrue% \let\exbank@isFirstProblem\isFalse% \else% \Trigger\EndProblem% \fi% \Trigger\BeginProblem% \Trigger\VeryBeginProblem% } \exbank@def@makeset@command{\pbreak}{\clearpage} \let\DeclareExerciseCommand\exbank@def@makeset@command \global\let\ex@protect\noexpand \gdef\exec#1{:\ex@protect{#1}} \gdef\@countIntros{\isFalse} \gdef\exb@setpgf#1{% \edef\keystring{{/makeset,default,#1}}% \ea\pgfkeys\keystring} \newcommand\buildset[2][]{% \def\oarg{#1}% \@ifundefined{setlist@#2@intro}{\gdef\@countIntros{\isFalse}}{\gdef\@countIntros{\isTrue}\@dlog{Counting intros}}% \@ifundefined{setlist@#2@nohead}{\gdef\nohead{\isFalse}}{\gdef\nohead{\isTrue}}% \xdef\setName{#2}% \ifcsname setkeys@\setName\endcsname% \xdef\skeys{\csname setkeys@#2\endcsname}% \exb@setpgf\skeys% \fi% \@ifundefined{setlist@#2}{% \@latex@error{Couldn't find set #2. Did you remember to do \string\makeset{#2}{}?} \stop\bye }{} \let\exlist@protect\unexpanded% \edef\exbank@setlist{\csname setlist@#2\endcsname}% \let\exlist@protect\noexpand% \if\@spriteMode\isFalse% \if\exb@frontpage\@isFalse\else\input{\exb@frontpage}\fi \Trigger\StartBuildset% \fi% \gdef\@delegateFileInfo{\@ifnextchar?\@execute@makeset@command\@is@exec}% \def\@is@exec{\@ifnextchar:\@execute@exec\@is@file}% \gdef\@is@file{\@ifnextchar[\exb@setEnv@withOptargs\X@exb@setEnv}% \gdef\X@exb@setEnv##1{% \def\continueLoop{\isTrue}% \@dlog{Processing normal file "\exerciseFileInfo.tex"}% \exbank@setEnv@normal{\exerciseFileInfo}\bgroup\nullfont% } \gdef\exb@setEnv@withOptargs{% \def\continueLoop{\isTrue}\ea\exbank@setEnv\exerciseFileInfo\bgroup\nullfont% } \gdef\@execute@makeset@command ?##1{% \edef\inner{\@firstofone##1}% \@dlog{Executing macro \@backslashchar\@gobble##1}% \csname\inner\endcsname% #1\def\continueLoop{\isFalse}\ea\bgroup% } \def\@execute@exec:{% \def\continueLoop{\isFalse}\@dlog{Executing custom command}\bgroup% } \readlist*\setlist\exbank@setlist% \foreachitem\exerciseFileInfo\in\setlist{% \def\continueLoop{\isTrue}% \gdef\ppList{\emptyList}% \setcounter{metacounter}{0}% \let\ex@protect\unexpanded% \edef\exerciseFileInfo{\exerciseFileInfo}% \ea\@delegateFileInfo\exerciseFileInfo\egroup% \Trigger\InputExercise% \if\continueLoop\isTrue% \let\ex@protect\noexpand% \Trigger\PathControl% \if\nohead\isFalse% \if\@spriteMode\isFalse% \Trigger\BeginProblem% \fi% \fi% \if\nohead\isFalse\Trigger\VeryBeginProblem\fi% \if\@spriteMode\isTrue\textbf{\exerciseFile.tex\\}\fi% \IfFileExists{\@exercisesDir/\exerciseFile}{% \incl{\@exercisesDir/\exerciseFile}% \if\@spriteMode\isTrue% \setcounter{partproblemcounter}{0}% \fi }{ \@latex@error{Could not find \@exercisesDir/\exerciseFile. Maybe it is because the default exercise directory is now changed to the same directory that your main file is in. To set default exercise directory to exercises, do \string\exercisebanksetup{exercise directory=exercises}}{} \stop\bye } \if\nohead\isFalse\Trigger\EndProblem\fi% \fi% } \if\@spriteMode\isFalse% \Trigger\EndBuildset% \fi \setcounter{problemcounter}{0}% \setcounter{partproblemcounter}{0}% \clearpage% } \newcommand\buildsets[1]{% \xdef\csvlist{#1}% \readlist*\items\csvlist% \foreachitem\set\in\items{% \buildset{\set}% } } \newcommand\refcounter[1]{\edef\@currentlabel{#1}% } \DeclareRobustCommand{\pplabel}[1]{ \refcounter{\theproblemcounter}\label{pr:\exerciseFile:#1} \refcounter{\alph{partproblemcounter}}\label{pp:\exerciseFile:#1} } \let\pptag = \pplabel \newcommand\pppref[1]{% (\ea\@alph\ref{pp:\exerciseFile:#1})\relax% } \newcommand\ppref[1]{% (\ref{pr:\exerciseFile:#1}\ref{pp:\exerciseFile:#1})\relax% } \newcommand\pref[1]{% \ref{pr:\exerciseFile:#1}\relax% }