Skip to content

Instantly share code, notes, and snippets.

@FiggChristian
Last active April 12, 2020 05:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FiggChristian/c06e64c823c3968a7ab7776ca75b7bcf to your computer and use it in GitHub Desktop.
Save FiggChristian/c06e64c823c3968a7ab7776ca75b7bcf to your computer and use it in GitHub Desktop.
Helper TeX file for CS 103 and CS 109 Stanford courses (and probably a bunch of other courses, but that's all I've taken so far).
% Filename: extras.tex
% Author: Christian Figueroa (cfigg@stanford.edu)
% Usage:
% 1. Drop this file in whatever directory your source .tex file is in.
% 2. Add "\include{extras}" before the "\begin{document}" line.
% 3. Change the \me command on the next line so that your paper actually says your name, not mine.
\newcommand\me{Christian Figueroa}%
\usepackage{xspace}%
\usepackage{expl3}%
\usepackage{environ}%
\usepackage{upgreek}%
\usetikzlibrary{calc}%
\ExplSyntaxOn%
\makeatletter%
%
%
%
% ~~~~~~~~~~~~~~~~~~~~~~~~
% User-Accessible Macros
% ~~~~~~~~~~~~~~~~~~~~~~~~
%
% \makecommand
% Exactly like \newcommand except it works even if the command is already defined.
\providecommand\makecommand{}%
\providecommand\@make@command{}%
\providecommand\@make@command@star{}%
\renewcommand\makecommand{\@ifstar\@make@command@star\@make@command}%
\renewcommand\@make@command[1]{\providecommand#1{}\renewcommand#1}%
\renewcommand\@make@command@star[1]{\providecommand*#1{}\renewcommand*#1}%
%
% \begin{indentpar} ... \end{indentpar}
% Environment for making an entire paragraph indented.
% Change the optional argument to any measurement to change the indentation amount (default = 0.25in).
\newenvironment{indentpar}[1][0.25in]{\makeline\\\begin{minipage}{\dimexpr\linewidth}\hskip#1\begin{minipage}{\dimexpr\linewidth-#1}\let\@oldPar=\\\def\\{\@oldPar[5pt]}\strut\ignorespaces}{\unskip\strut\end{minipage}\end{minipage}\\}%
%
% Modify prf environment.
% Adds \define command that is equivalent to \mathdef (since proofs typically use a lot of math symbols).
% Adds \begin{case} ... \end{case} environment that is identical to \begin{indentpar} ... \end{indentpar}
\makecommand*\@prf{%
\let\define=\mathdef%
\let\case=\indentpar%
\let\endcase=\endindentpar%
}%
\makecommand*\qedsymbol{\@wrapmath{\blacksquare}}%
\ifcsname prf\endcsname\expandafter\renewenvironment\else\expandafter\newenvironment\fi{prf}{{\bfseries Proof.}\@prf}{\qedsymbol}%
\newenvironment{prf*}{{\bfseries Proof.}\@prf}{}%
\newenvironment{dprf}{{\bfseries Disproof.}\@prf}{\qedsymbol}%
\newenvironment{dprf*}{{\bfseries Disproof.}\@prf}{}%
%
% \fulldef
% Command used to \def commands that contain non-letter characters in their name, like \A123.
% First agument is the name of the macro that will be \def-ed (as plain characters, not a \macro), can contain non-letter characters like _ or 1.
% Second argument is the macro's template and parameters (everything that goes between the macro name and the definition, like "#1").
% Third argument is the macro's body.
% Example: \fulldef{macro1}{(#1)}{$macro_1(#1)$}
% - Makes a macro called via "\macro1" ("\macro" can still be its own completely different macro).
% - \macro1 is called with parentheses around its argument as in \macro1(3) since the second argument is "(#1)".
% - \macro1(3) expands to "$\macro_1(3)$".
% This uses \def, not \newcommand, so no error checking is done the way \newcommand would.
% This *should* work for macros that have another macro as a prefix (e.g. \macro, \macro1, and \macro12 can all be different), as long \fulldef is used to define all of them (e.g. \fulldef{macro12}{}{(12)}, then \fulldef{macro1}{}{(1)} works, but \def\macro1{(1)} will break \macro12).
\makecommand\fulldef[3]{%
% Clear token list to start a new macro.
\tl_clear:N\@current@built@up@macro@name%
%
% Switch that keeps track of if a non-letter has been encountered or not.
% LaTeX will read the first part of a command that starts with letters, but then after that, each subsequent character has to be evaluated manually.
\let\if@reached@nonletter=\iffalse%
%
% Iterate over each token in the command name.
\tl_map_inline:nn{#1}{%
% If we're still absorbing letters.
\if@reached@nonletter\else%
% Check if the current token is still a letter.
\ifcat \c_catcode_letter_token##1%
\tl_put_right:Nn\@current@built@up@macro@name{##1}%
\else%
\let\if@reached@nonletter=\iftrue\@gobble\fi\@gobble\fi%
\fi%
\fi%
%
% If a non-letter has been reached.
\if@reached@nonletter%
% Macro to hold another macro name.
\expandafter\def\expandafter\@check@macro@name\expandafter{\@current@built@up@macro@name @check@macro}%
% If that macro does not exist yet, make a new one that will call the original macro.
\ifcsname\@check@macro@name\endcsname\else%
% This \[...]@root command represents what gets called when a macro gets called without the matching characters (e.g. \A2 calls \A, checks if an \A2 exists, and calls this (\A) if no \A2 exists).
\withexpansion{%
\def\expand\csname\@check@macro@name @root\endcsname####1{%
\expandafter\expand\csname @fulldef@orig@arg@\@current@built@up@macro@name\endcsname\expandafter{\expandafter####1\expandafter}%
}%
}%
% This is a temporary pointer to the @root that will be overwritten later.
\withexpansion{%
\def\expand\csname\@check@macro@name\endcsname{\expand\csname\@check@macro@name @root\endcsname}%
}%
\fi%
%
% Check if another macro has already been made that starts like this one (e.g. making \A12 after a macro like \A1 has already been made).
% If there is one, there's no use repeating this portion since it's already been done.
% Instead, go to the next character.
\ifcsname\@check@macro@name @##1\endcsname\else%
% Make a macro that checks if the next character matches what was used here.
% If it does, it calls that macro (e.g. \A1 needs to call \A, check if what follows is a 1, and then call "\A1").
% If it doesnt, it calls the macro without the character (e.g. \A2 calls \A, fails, then calls "\A" followed by a 2).
%
% This command is what gets called if the check fails. If there are other checks to perform (e.g. \A1 and \A2 needs to check for both 1 and 2), the net check gets performed. Otherwise, it calls the original root macro (\A in the example).
\withexpansion{%
\def\expand\csname\@check@macro@name @##1@else\endcsname{%
\expandafter\expand\expand\csname\expandafter\@check@macro@name\endcsname%
}%
}%
% Makes sure that a macro that is undefined stays undefined after being used in a \csname expression.
\def\@fulldef@reset@macro{true}%
\ifcsname\@current@built@up@macro@name##1\endcsname%
\def\@fulldef@reset@macro{false}%
\fi%
% Makes a macro that checks for one specific character depending on what's next in the macro name.
% If the check fails, the macro defined above gets called.
\withexpansion{%
\def\expand\csname\@check@macro@name @##1\endcsname####1{%
\if##1####1%
\expandafter\expand\csname\@current@built@up@macro@name##1\endcsname%
\else%
\expand\expand\csname\@check@macro@name @##1@else\endcsname\expandafter{\expandafter####1\expandafter}%
\fi%
}%
}%
% Make the macro go back to undefined if it was undefined to begin with.
\@gobble\if\csname if\@fulldef@reset@macro\endcsname%
\expandafter\let\csname\@current@built@up@macro@name##1\endcsname=\undefined%
\fi%
% Updates a pointer to look at the new checking macro we just defined.
\withexpansion{%
\def\expand\csname\@check@macro@name\endcsname{\expand\csname\@check@macro@name @##1\endcsname}%
}%
%
%
% Make \@fulldef@orig@[original macro]=[original macro] so that [original macro] can be redefined.
% Example: If \A and \A1 are both defined, the \A macro needs to be redefined to perform a check for a "1".
% If it fails, it calls what \A *used* to be, which is stored in \@fulldef@orig@A.
% If a \fulldef@orig@ macro already exists (e.g. defining \A2 after already making \A1, \A already exists), point to that one instead.
% If no original macro is defined (e.g. \A1 exists, but \A was never defined), this will raise an error saying \@fulldef@orig@A macro is not defined to show the programmer they probably mistyped somthing.
\ifcsname @fulldef@orig@\@current@built@up@macro@name\endcsname%
\expandafter\let\expandafter\@fulldef@old@assignment\expandafter=\csname @fulldef@orig@\@current@built@up@macro@name\endcsname%
\else%
\ifcsname\@current@built@up@macro@name\endcsname%
\expandafter\let\expandafter\@fulldef@old@assignment\expandafter=\csname\@current@built@up@macro@name\endcsname%
\fi%
\fi%
% Make \@fulldef@orig@arg@[original macro].
% This is dentical to the one defined above, except it takes one parameter and places it right in front.
% This is used for the character-checking macro, since that macro always takes one parameter (the character to check), and expects that this one will too if the check fails.
\withexpansion{%
\def\expand\csname @fulldef@orig@arg@\@current@built@up@macro@name\endcsname####1{\expand\csname @fulldef@orig@\@current@built@up@macro@name\endcsname####1}%
}%
\expandafter\let\csname @fulldef@orig@\@current@built@up@macro@name\endcsname=\@fulldef@old@assignment%
%
% Now the original user-accessible macro (like the "\A" for \A1 and \A2) can be reassigned to point to the check macro (actually, it points to the pointer that points to the check macro, but same thing).
\expandafter\expandafter\expandafter\def\expandafter\csname\expandafter\@current@built@up@macro@name\expandafter\endcsname\expandafter{\csname\@current@built@up@macro@name @check@macro\endcsname}%
\fi%
% Adds the character we just parsed to the list of "done" tokens.
\tl_put_right:Nn\@current@built@up@macro@name{##1}%
\fi%
}%
%
% Now that all the preceding macros have been defined, the final macro can be defined.
% Example: defining \A123 requires defining \A, \A1, and \A12 before \A123 can be defined.
\ifcsname\@current@built@up@macro@name @check@macro\endcsname%
\expandafter\def\csname @fulldef@orig@\@current@built@up@macro@name\endcsname#2{#3}%
\else%
\expandafter\def\csname\@current@built@up@macro@name\endcsname#2{#3}%
\fi%
% Ignore any spaces so that \fulldef can be used on its own line without making more spaces.
\ignorespaces%
}%
%
% Token list used in \fulldef that keeps track of tokens that have already been parsed.
\tl_new:N\@current@built@up@macro@name%
%
% \abox, \mabox, \habox
% \abox makes two \fbox-es around its argument to indicate an answer. It wraps its argument in math delimiters if the surrounding is in math mode.
% \mabox does the same except it always wraps its argument in math delimiters.
% \habox does the same except it removes math delimiters if there are any.
\makecommand\abox[1]{{\setlength{\fboxsep}{.75pt}\setlength{\fboxrule}{.75pt}\ifmmode\mathchoice{\fbox{\setlength{\fboxsep}{1pt}\fbox{$\displaystyle\mathstrut#1$}}}{\fbox{\setlength{\fboxsep}{1pt}\fbox{$\textstyle\mathstrut#1$}}}{\fbox{\setlength{\fboxsep}{1pt}\fbox{$\scriptstyle\mathstrut#1$}}}{\fbox{\setlength{\fboxsep}{1pt}\fbox{$\scriptscriptstyle\mathstrut#1$}}}\else\fbox{\setlength{\fboxsep}{1pt}\fbox{\strut#1}}\fi}}%
\makecommand\mabox[1]{\@wrapmath{\abox{#1}}}%
\makecommand\habox[1]{{\setlength{\fboxsep}{.75pt}\setlength{\fboxrule}{.75pt}\fbox{\setlength{\fboxsep}{1pt}\fbox{\strut#1}}}}%
% \fracabox and \binomabox
% Shortcut for wrapping \mabox around \frac and \binom.
\makecommand*\fracabox[2]{\mabox{\frac{\mathstrut#1}{\mathstrut#2}}}
\makecommand*\binomabox[2]{\mabox{\binom{#1}{#2}}}
%
% \Ans
% Used in mathmode to indicate the previous answer; expands to just \mathrm{Ans}
\makecommand*\Ans{\mathrm{Ans}}
%
% \widthOf
% Used in \hbox expressions.
% Example: \hbox to \widthOf{test}{something}
% Makes an hbox with the width of its argument ("test" in the example above).
\newbox\@widthOf@temp@box%
\makecommand\widthOf[1]{0pt{}\setbox\@widthOf@temp@box=\hbox{#1}\hbox to \wd\@widthOf@temp@box}%
%
% \noheight
% Makes its argument not have any height (depth is preserved).
% Use with \nodepth (i.e. \noheight{\nodepth{ ... }}) to combine both.
\makecommand\noheight[1]{\ifmmode\mathchoice{\raisebox{0pt}[0pt][\depth]{$\displaystyle#1$}}{\raisebox{0pt}[0pt][\depth]{$\textstyle#1$}}{\raisebox{0pt}[0pt][\depth]{$\scriptstyle#1$}}{\raisebox{0pt}[0pt][\depth]{$\scriptscriptstyle#1$}}\else\raisebox{0pt}[0pt][\depth]{#1}\fi}%
% \nodepth
% Makes its argument not have any depth (height is preserved).
% Use with \noheight (i.e. \nodepth{\noheight{ ... }}) to combine both.
\makecommand\nodepth[1]{\ifmmode\mathchoice{\raisebox{0pt}[\height][0pt]{$\displaystyle#1$}}{\raisebox{0pt}[\height][0pt]{$\textstyle#1$}}{\raisebox{0pt}[\height][0pt]{$\scriptstyle#1$}}{\raisebox{0pt}[\height][0pt]{$\scriptscriptstyle#1$}}\else\raisebox{0pt}[\height][0pt]{#1}\fi}%
%
% \makeline
% Makes an empty line.
% Used when "\\" would normally give an error about not having a line to end.
\makecommand*\makeline{$\quad$\hbox to -1em{}}%
%
% \set
% Makes a set using set-builder notation.
% Example: \set{ x | x \in \N }
% Sets must contain a "|" character (used for parsing).
% \and and \or macros are also defined that expand to " and " and " or ".
% For example: \set{ x | 0 < x \and x \in \N }.
\def\set#{\set@build}%
\def\set@build#1{\set@@build#1\@nil}%
\def\set@@build#1|#2\@nil{{\def\or{}\@wrapmath{\left\{#1\mathrel{}\middle|\mathrel{}{\def\and{\mathrm{\ and\ }}\def\or{\mathrm{\ or\ }}#2}\right\}}}}%
%
% \begin{seq} ... \end{seq} and \begin{seq*} ... \end{seq*}
% Shortcut for "equation" and "split" environements combined.
% seq* produces unnumbered equations.
\NewEnviron{seq}{\begin{equation}\begin{split}{}\BODY\end{split}\end{equation}}%
\NewEnviron{seq*}{\begin{equation*}\begin{split}{}\BODY\end{split}\end{equation*}}%
%
% \while
% Equivalent to \if except the body is run over and over until the condition is false.
% Each variant of \if has its own corresponding \while.
% Defines a \break macro that breaks the loop early.
% This macro is pretty easy to break so don't depend on it for anything important :).
\def\while#1\fi{\@while\if{#1}}%
\def\whilecat#1\fi{\@while\ifcat{#1}}%
\def\whiledim#1\fi{\@while\ifdim{#1}}%
\def\whileeof#1\fi{\@while\ifeof{#1}}%
\def\whilefalse#1\fi{\@while\iffalse{#1}}%
\def\whilehbox#1\fi{\@while\ifhbox{#1}}%
\def\whilehmode#1\fi{\@while\ifhmode{#1}}%
\def\whileinner#1\fi{\@while\ifinner{#1}}%
\def\whilemmode#1\fi{\@while\ifmmode{#1}}%
\def\whilenum#1\fi{\@while\ifnum{#1}}%
\def\whileodd#1\fi{\@while\ifodd{#1}}%
\def\whiletrue#1\fi{\@while\iftrue{#1}}%
\def\whilevbox#1\fi{\@while\ifvbox{#1}}%
\def\whilevmode#1\fi{\@while\ifvmode{#1}}%
\def\whilevoid#1\fi{\@while\ifvoid{#1}}%
\def\whilex#1\fi{\@while\ifx{#1}}%
\makecommand\@while[2]{\makecommand\break{\iffalse}#1#2\@while#1{#2}\fi}%
%
% \withexpansion
% Entire gist dedicated to documenting this one:
% https://gist.github.com/ChristianFigueroa/ad409a3ad3008b2478e9ff6d88716fe0#gistcomment-3235661
% Works by making the argument into a token list, reversing it, iterating over them and counting "\expand" tokens, and adding "\expandafter" tokens between each token as more "\expand" tokens are encountered.
\makecommand\withexpansion[2][\expand]{%
\edef\@withexpansion@expand@compare{\detokenize{#1}}%
\@withexpansion@expansion@count=0%
\tl_set:Nn\@withexpansion@tl@one{#2}%
\tl_reverse:N\@withexpansion@tl@one%
\tl_clear:N\@withexpansion@tl@two%
\expandafter\@withexpansion@while\expandafter{\@withexpansion@tl@one}%
\regex_replace_all:nnN{\c{@withexpansion@space@token}}{\ }\@withexpansion@tl@two%
\@withexpansion@tl@two%
}%
\makecommand\withexpansion@debug[2][\expand]{%
\edef\@withexpansion@expand@compare{\detokenize{#1}}%
\@withexpansion@expansion@count=0%
\tl_set:Nn\@withexpansion@tl@one{#2}%
\tl_reverse:N\@withexpansion@tl@one%
\tl_clear:N\@withexpansion@tl@two%
\expandafter\@withexpansion@while\expandafter{\@withexpansion@tl@one}%
\regex_replace_all:nnN{\c{@withexpansion@space@token}}{\ }\@withexpansion@tl@two%
\tl_show:N\@withexpansion@tl@two%
}%
% Where most of the actual work for \withexpansion takes place.
\makecommand\@withexpansion@while[1]{%
\edef\@withexpansion@head{\tl_head:N\@withexpansion@tl@one}%
\tl_if_head_is_space:nTF{#1}{%
\tl_put_left:Nn\@withexpansion@tl@two{\@withexpansion@space@token}%
\edef\@withexpansion@intexpr{\the\numexpr\@withexpansion@make@intexpr{0}{\@withexpansion@expansion@count}1-1\relax}%
\edef\@withexpansion@expansions{\@withexpansion@make@expansions{0}{\@withexpansion@intexpr}}%
\tl_put_left:NV\@withexpansion@tl@two\@withexpansion@expansions%
\tl_trim_spaces:N\@withexpansion@tl@one%
\tl_put_left:Nn\@withexpansion@tl@one{\@withexpansion@nul}%
}{%
\tl_if_head_eq_catcode:oNTF\@withexpansion@tl@one\c_group_begin_token{%
\edef\@withexpansion@head@detokenized{\expandafter\detokenize\expandafter{\@withexpansion@head}}%
\ifx\@withexpansion@head@detokenized\@withexpansion@bgroup@compare%
\tl_clear:N\@withexpansion@tl@tre%
\expandafter\@withexpansion@@while\expandafter{\@withexpansion@tl@two}%
\tl_put_left:No\@withexpansion@tl@two{\expandafter{\@withexpansion@tl@tre}}%
\edef\@withexpansion@intexpr{\the\numexpr\@withexpansion@make@intexpr{0}{\@withexpansion@expansion@count}1-1\relax}%
\edef\@withexpansion@expansions{\@withexpansion@make@expansions{0}{\@withexpansion@intexpr}}%
\tl_put_left:NV\@withexpansion@tl@two\@withexpansion@expansions%
\else%
\expandafter\tl_if_head_is_N_type:nTF\expandafter{\@withexpansion@tl@one}{%
\tl_put_left:NV\@withexpansion@tl@two\@withexpansion@head%
\edef\@withexpansion@intexpr{\the\numexpr\@withexpansion@make@intexpr{0}{\@withexpansion@expansion@count}1-1\relax}%
\edef\@withexpansion@expansions{\@withexpansion@make@expansions{0}{\@withexpansion@intexpr}}%
\tl_put_left:NV\@withexpansion@tl@two\@withexpansion@expansions%
}{%
\tl_reverse:N\@withexpansion@head%
\tl_set:Nx\@withexpansion@tl@one{\tl_tail:N\@withexpansion@tl@one}%
\tl_put_left:Nn\@withexpansion@tl@one{\@withexpansion@group@begin@token}%
\tl_put_left:NV\@withexpansion@tl@one{\@withexpansion@head}%
\tl_put_left:Nn\@withexpansion@tl@one{\@withexpansion@group@end@token}%
\tl_put_left:Nn\@withexpansion@tl@one{\@withexpansion@nul}%
}%
\fi%
}{%
\token_if_macro:NTF\@withexpansion@head{%
\edef\@withexpansion@head@detokenized{\expandafter\detokenize\expandafter{\@withexpansion@head}}%
\ifx\@withexpansion@head@detokenized\@withexpansion@expand@compare%
\advance\@withexpansion@expansion@count by1%
\else%
\tl_put_left:NV\@withexpansion@tl@two\@withexpansion@head%
\edef\@withexpansion@intexpr{\the\numexpr\@withexpansion@make@intexpr{0}{\@withexpansion@expansion@count}1-1\relax}%
\edef\@withexpansion@expansions{\@withexpansion@make@expansions{0}{\@withexpansion@intexpr}}%
\tl_put_left:NV\@withexpansion@tl@two\@withexpansion@expansions%
\fi%
}{%
\tl_put_left:NV\@withexpansion@tl@two\@withexpansion@head%
\edef\@withexpansion@intexpr{\the\numexpr\@withexpansion@make@intexpr{0}{\@withexpansion@expansion@count}1-1\relax}%
\edef\@withexpansion@expansions{\@withexpansion@make@expansions{0}{\@withexpansion@intexpr}}%
\tl_put_left:NV\@withexpansion@tl@two\@withexpansion@expansions%
}%
}%
}%
\tl_set:Nx\@withexpansion@tl@one{\tl_tail:N\@withexpansion@tl@one}%
\tl_if_blank:VTF\@withexpansion@tl@one{}{\expandafter\@withexpansion@while\expandafter{\@withexpansion@tl@one}}%
}%
% Used for dealing with groups (i.e. { ... }) in token lists.
\makecommand\@withexpansion@@while[1]{%
\tl_if_head_is_group:nTF{#1}{%
\edef\@withexpansion@@head{\tl_head:N\@withexpansion@tl@two}%
\tl_put_left:No\@withexpansion@tl@tre{\expandafter{\@withexpansion@@head}}%
\tl_set:Nx\@withexpansion@tl@two{\tl_tail:N\@withexpansion@tl@two}%
\expandafter\@withexpansion@@while\expandafter{\@withexpansion@tl@two}%
}{%
\tl_put_left:Nx\@withexpansion@tl@tre{\tl_head:N\@withexpansion@tl@two}%
\tl_set:Nx\@withexpansion@tl@two{\tl_tail:N\@withexpansion@tl@two}%
\edef\@withexpansion@@head{\tl_head:N\@withexpansion@tl@tre}%
\edef\@withexpansion@@head{\expandafter\detokenize\expandafter{\@withexpansion@@head}}%
\ifx\@withexpansion@@head\@withexpansion@egroup@compare%
\edef\@withexpansion@tl@tre{\tl_tail:N\@withexpansion@tl@tre}%
\tl_reverse:N\@withexpansion@tl@tre%
\else%
\expandafter\@withexpansion@@while\expandafter{\@withexpansion@tl@two}%
\fi%
}%
}%
% Extra stuff used in \withexpansion.
\edef\@withexpansion@opener{\char_generate:nn{`\{}{12}}%
\let\@withexpansion@group@begin@token={%
\let\@withexpansion@group@end@token=}%
\edef\@withexpansion@bgroup@compare{\detokenize{\@withexpansion@group@begin@token}}%
\edef\@withexpansion@egroup@compare{\detokenize{\@withexpansion@group@end@token}}%
\tl_new:N\@withexpansion@tl@one%
\tl_new:N\@withexpansion@tl@two%
\tl_new:N\@withexpansion@tl@tre%
\newcount\@withexpansion@expansion@count%
\makecommand\@withexpansion@make@intexpr[2]{\ifnum#1<#2 2*\@withexpansion@make@intexpr{\numexpr#1+1\relax}{#2}\fi}%
\makecommand\@withexpansion@make@expansions[2]{\ifnum#1<#2 \noexpand\noexpand\expandafter\@withexpansion@make@expansions{\numexpr#1+1\relax}{#2}\fi}%
%
% \mathdef and \mathdef*
% \mathdef takes a comma-separated list of macros to define (in the form of {name1=definition1, name2=definition2}).
% Each definition in the list makes a macro with the given name and definition, except it is expanded as if in math mode.
% Example: \mathdef{a=\alpha} would make a macro \a that expands to "$\alpha$", or just "\alpha" (depending on if the $ are needed).
% A macro name with no definition (e.g. {a}) will make a macro \a that expands to "\mathit{a}" (i.e., the name is also the definition, wrapped in \mathit{} to correct spacing).
% A definition can also start with "\func" (e.g. {f=\func g}) to produce a macro \f(#1) that expands to "g\left(#1\right)" (like a function call).
% If the definition is just "\func" (e.g. {f=\func}), the name is used instead (i.e. {f=\func} makes \f(#1) expand to "\mathit{f}\left(#1\right)").
% Since \fulldef is used, the name of a macro can contain non-letter characters (e.g. {a1=alpha_1, a2=\alpha_2}).
% Multiple equal signs in a definition (e.g. {a=b=c}) will only use the leftmost one (i.e. {a=b=c} makes a macro \a that produces "b=c").
% The \mathdef* command does the same, except that macros are not separated into comma-separated lists (i.e. \mathdef*{a=a,b=b} would make a macro \a that expands to "a,b=b") (useful for if a comma is needed in the definition).
\makecommand\mathdef{\@ifstar\@mathdef\@mathdef@clist}%
\makecommand\@mathdef[1]{%
\seq_set_split:Nnn\@mathdef@definition{=}{#1}%
\edef\@mathdef@count{\seq_count:N\@mathdef@definition}%
\edef\@mathdef@one{1}%
\ifx\@mathdef@count\@mathdef@one%
\seq_map_inline:Nn\@mathdef@definition{%
\fulldef{#1}{}{\@wrapmath{\mathit{#1}}\xspace}%
}%
\else%
\seq_pop_left:NN\@mathdef@definition\@mathdef@name%
\edef\@mathdef@definition{\seq_use:Nn\@mathdef@definition{=}}%
\tl_set:NV\l_tmpa_tl\@mathdef@definition%
\edef\@mathdef@head@toks{\tl_head:N\l_tmpa_tl}%
\edef\@mathdef@head@toks{\expandafter\detokenize\expandafter{\@mathdef@head@toks}}%
\edef\@mathdef@func@comp{\detokenize{\func}}%
\ifx\@mathdef@head@toks\@mathdef@func@comp%
\tl_set:Nx\l_tmpb_tl{\tl_tail:N\l_tmpa_tl}%
\tl_if_blank:VTF\l_tmpb_tl{\expandafter\def\expandafter\@mathdef@definition\expandafter{\expandafter\mathit\expandafter{\@mathdef@name}}}{\edef\@mathdef@definition{\tl_use:N\l_tmpb_tl}}%
\withexpansion{%
\fulldef{\expand\@mathdef@name}{(##1)}{\@wrapmath{{\expand\@mathdef@definition}{\left(##1\right)}}\xspace}
}%
\else%
\withexpansion{%
\fulldef{\expand\@mathdef@name}{}{\@wrapmath{\expand\@mathdef@definition}\xspace}%
}%
\fi%
\fi%
}%
\makecommand\@mathdef@clist[1]{%
\clist_map_function:nN{#1}\@mathdef%
\ignorespaces%
}%
%
%
%
% ~~~~~~~~~~~~~~~~~~~
% Extra Setup Stuff
% ~~~~~~~~~~~~~~~~~~~
%
% \@wrapmath
% Wraps math delimiters around its argument if not already in math mode.
\makecommand*\@wrapmath[1]{\ifmmode#1\else\math#1\endmath\fi}
%
% Make \binom and \frac usable on their own without math delimiters.
% If any other character are needed (e.g. "+" in "\frac{}{} + \frac{}{}"), use regular math delimiters instead.
\let\@orig@binom=\binom%
\makecommand*\binom[2]{\@wrapmath{\@orig@binom{#1}{#2}}}%
\let\@orig@frac=\frac%
\makecommand*\frac[2]{\@wrapmath{\@orig@frac{#1}{#2}}}%
%
% \begin{answer} ... \end{answer} environment make a line after the "Answer." text.
\let\@answer=\answer%
\let\end@answer=\endanswer%
\renewenvironment{answer}{\begin{@answer}\makeline}{\end{@answer}}
\newenvironment{answer*}{\begin{@answer}}{\end{@answer}}
%
% \begin{shaded} ... \end{answer} environment modification to make \\ line breaks wider and more noticeable.
% Use \returnCr to make \\ go back to 0pt or just use \par since those remain unchanged.
\let\@shaded=\shaded%
\let\end@shaded=\endshaded%
\renewenvironment{shaded}{\begin{@shaded}\setcounter{equation}{0}\let\@oldPar=\\\def\\{\@oldPar[5pt]}\makecommand\returnCr{\let\\=\@oldPar}}{\end{@shaded}}%
%
% Make logical operators relations.
\mathchardef\land="325E%
\mathchardef\lor="325F%
%
% Make a \canceled relation have the spacing of a real relation.
\ifcsname cancel\endcsname\let\@old@cancel=\cancel\renewcommand\cancel[1]{\mathrel{\@old@cancel{#1}}}\fi%
%
% Make the left header and author say your name.
\author{\me}%
\lhead{\me}%
\ExplSyntaxOff%
\makeatother%
@FiggChristian
Copy link
Author

extras.tex

As the header at the beginning of the file says, use this file via \include{extras} before your \begin{document} tag.

I started making this when I took CS 103 and added little shortcuts out of laziness until eventually it became this monstrosity, and now I continue to use it for any math class that uses LaTeX. Maybe some other people might get some use out of it too (whether you're taking CS courses at Stanford or not), so here it is.

Even though there are a bunch of comments for most of the file to explain what's happening, here's the basic gist (pun definitely intended) of what's included in the file:

\fulldef

Defines a macro that can contain non-letter characters in its name, like \macro123.

\fulldef{#1}{#2}{#3}

  • #1 – The name of the macro to define, as plain tokens, not as a \macroName.
  • #2 – The parameter text of the macro (what appears after the name and before the body as in \def\name(#1){body}).
  • #3 – The body of the macro.
\fulldef{squareOf}{#1}{$#1^2$}
\fulldef{squareOf1}{}{one}
\fulldef{squareOf2}{}{four}
\fulldef{squareOf12}{}{one hundred forty-four}
\squareOf1   %  "one"
\squareOf2   %  "four"
\squareOf3   %  "$3^2$"
\squareOf4   %  "$4^2$"
\squareOf12  %  "one hundred forty-four"

Notice that \squareOf is a macro on its own (equivalent to #1->$#1^2$), while \squareOf1 is a completely different macro with the entire name \squareOf1. \squareOf12 is also its own macro completely different from the others. Even though \squareOf12 is prefixed by \squareOf1, it still checks for the following 2 and uses \squareOf12 instead.

BUG: Since the mechanism behind this checks for the next character after the macro, (to differentiate between \squareOf1 and \squareOf12), you should try to avoid placing one of these macros right before an \end{ ... }. For example:

\begin{environ}
    \fulldef{squareOf1}{}{one}
    \fulldef{squareOf12}{}{one hundred forty-four}
   \squareOf1  % Tries to look for a "2" and accidentally absorbs the \end.
\end{environ}

A super easy fix is to just add anything (like a period at the end of a sentence) or use a macro that doesn't check for more characters:

\begin{environ}
    \fulldef{squareOf1}{}{one}
    \fulldef{squareOf12}{}{one hundred forty-four}
    The square of one is \squareOf1.   % Finds a period and stops looking for a "2".
    \squareOf12  % Also works since there's no other character left to look for, so \end never gets looked at.
\end{environ}

\mathdef

Makes a math macro that can be used in or out of math mode.

\mathdef{#1}

  • #1 – A comma-separated list of macros to define.

The list of macros are in the form of \mathdef{ name=definition }. The name of the macro is again plain tokens instead of as a \macroName. The definition is expanded in math mode.

Example: \mathdef{ a=\alpha, b=\beta } would make a macro \a that expands to either $\alpha$ or just \alpha (depending on if the $s are necessary or not). \b would expand to \beta as expected.

The definition can also be omitted altogether. \mathdef{ name, otherName } for example would make two macros \name -> $\mathit{name}$ and \otherName -> $\mathit{otherName}$. The name of the macro is wrapped in \mathit{ ... } and used as the definition too. This came in helpful for writing proofs since you can make one declaration at the beginning of all the variables you'll use and then just refer to them as \n instead of as $n$.

Since \mathdef uses \fulldef, you can do something like \mathdef{ a1=\alpha_1, a2=\alpha_2 } to make \a1 and \a2 shortcuts for α1 and α2.

Alternatively, you can use the \func keyword to make macro functions. \mathdef{ P=\func, P1=\func P_1, P2=\func P_2 } makes three macros: \P(#1) -> $\mathit{P}\left(#1\right)$ (used like \P(1) to make the math mode version $\mathit{P}\left(1\right)$, which looks like a function call to P), \P1(#1) -> $P_1\left(#1\right)$, and \P2(#1) -> $P_2\left(#1\right)$. Notice that without anything after the \func, the name of the macro is used as the name of the function, wrapped in \mathit{ ... }. Otherwise, the text after the \func is used (without wrapping in \mathit{ ... }, so it has to be included manually).

Use \mathdef*{ ... } (i.e. the starred version) to ignore commas. Something like \mathdef*{a=a,b=b} would ignore the comma and make a macro \a that expands to a,b=b. This is useful for if you every need a comma in your definition for whatever reason.

\withexpansion

This one got its own gist with documentation: https://gist.github.com/ChristianFigueroa/ad409a3ad3008b2478e9ff6d88716fe0.

\widthOf

Makes an \hbox with the width of its specified argument.

\widthOf is meant to be placed in an expression like \hbox to \widthOf{width}{lorem ipsum}, which would make an \hbox with the width of the text "width", but that has "lorem ipsum" as the text inside the box. This only works for \hbox, so change its definition if you need it for anything else.

\noheight and \nodepth

Makes its argument not take up any height or depth, respectively. Use both in conjunction (i.e. \noheight{\nodepth{ ... }}) to not take up any height or depth at all.

\set

Makes a set using set-builder notation.

The argument must be in the form { ... | ... } and will be typeset in math mode whether the \set appears in $ delimiters or not. \set{ x | x \in \N \and x > 3} would produce "{𝑥 | 𝑥 ∈ ℕ and 𝑥 > 3}".

\begin{indentpar} ... \end{indentpar}

Makes an indented paragraph (or paragraphs). This is useful in proofs for introducing a new level of importance such as:

Proof. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Fermentum odio eu feugiat pretium nibh ipsum.

Case that something is true:
\begin{indentpar}
      Adipiscing tristique risus nec feugiat. Etiam tempor orci eu lobortis elementum nibh. Tincidunt eget nullam non nisi est sit amet facilisis.
      Nullam eget felis eget nunc. Sit amet aliquam id diam.
\end{indentpar}

Case that something is false:
\begin{indentpar}
      Massa id neque aliquam vestibulum morbi blandit. Donec ac odio tempor orci. Euismod nisi porta lorem mollis aliquam ut. Amet tellus
      cras adipiscing enim eu turpis.
\end{indentpar}

Sit amet massa vitae tortor condimentum. Orci a scelerisque purus semper. Turpis cursus in hac habitasse platea dictumst quisque sagittis. Risus quis varius quam quisque id diam vel quam. In eu mi bibendum neque egestas congue. ■

\abox

Makes a double box around its argument to grab the attention of the reader. The a in \abox stands for "answer" since I like to show my work and then put a box around my answer for the person grading it.

Since you usually use this for math, there's also a \mabox (math-mode answer box) macro that can be used to make sure its argument is displayed in math mode even if the \mabox isn't in $ math delimiters. There's also \habox (horizontal-mode answer box) for making sure that its argument is written as plain text, not in math mode, even if the \habox is in $ math delimiters.

Also, since I'm doing CS 109 (probability), a lot of my answers end up being a fraction (\frac) or binomial coefficient syntax (\binom), so I made \fracabox and \binomabox that are the same as \frac and \binom, except they get a \mabox wrapped around them.

Smaller stuff I'm not gonna mention

... since the file is literally right there, so look at the comments because these are pretty simple additions:

  • \makecommand
  • \Ans
  • \makeline
  • \begin{seq} ... \end{seq}
  • \while
  • Made \frac and \binom usable outside of math mode
  • Modified the prf, shaded, and answer environments
  • Made some characters have the proper spacing in math mode (as well as \cancelled relations)
  • Made the \author and \lhead always say the same name
  • Always import calc library for tikz.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment