% \iffalse meta-comment
% !TEX program = lualatexmk
%
% Copyright (C) 2021-2024 by Paul J. Heafner <heafnerj@gmail.com>
% ---------------------------------------------------------------------------
% 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
%            https://www.latex-project.org/lppl.txt
% and  version 1.3c 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 Paul J. Heafner.
%
% This work consists of the files mandi.dtx
%                                 mandistudent.dtx
%                                 mandiexp.dtx
%                                 mandi.ins
%                                 mandi.pdf
%                                 README.md
%
% and includes the derived files  mandi.sty
%                                 mandistudent.sty
%                                 mandiexp.sty
%                                 vdemo.py (not needed)
% ---------------------------------------------------------------------------
%
%<*internal>
\iffalse
%</internal>
%
%<*vdemo>
from vpython import *

scene.width = 400
scene.height = 760
# constants and data
g = 9.8       # m/s^2
mball = 0.03  # kg
Lo = 0.26     # m
ks = 1.8      # N/m
deltat = 0.01 # s 

# objects (origin is at ceiling)
ceiling = box(pos=vector(0,0,0), length=0.2, height=0.01, 
              width=0.2)
ball = sphere(pos=vector(0,-0.3,0),radius=0.025,
              color=color.orange)
spring = helix(pos=ceiling.pos, axis=ball.pos-ceiling.pos,
               color=color.cyan,thickness=0.003,coils=40,
               radius=0.010)

# initial values
pball = mball * vector(0,0,0)      # kg m/s
Fgrav = mball * g * vector(0,-1,0) # N
t = 0

# improve the display
scene.autoscale = False        # turn off automatic camera zoom
scene.center = vector(0,-Lo,0) # move camera down
scene.waitfor('click')         # wait for a mouse click

# initial calculation loop
# calculation loop
while t < 10:
    rate(100)
    # we need the stretch
    s = mag(ball.pos) - Lo
    # we need the spring force
    Fspring = ks * s * -norm(spring.axis)
    Fnet = Fgrav + Fspring
    pball = pball + Fnet * deltat
    ball.pos = ball.pos + (pball / mball) * deltat
    spring.axis = ball.pos - ceiling.pos
    t = t + deltat
%</vdemo>
%
%<*internal>
\fi
\def\nameofplainTeX{plain}
\ifx\fmtname\nameofplainTeX\else
  \expandafter\begingroup
\fi
%</internal>
%
%<*internal>
\usedir{tex/latex/mandi}
\ifx\fmtname\nameofplainTeX
  \expandafter\endbatchfile
\else
  \expandafter\endgroup
\fi
%</internal>
%
%<*driver>
\ProvidesFile{mandistudent.dtx}
\DisableCrossrefs         % index descriptions only
\PageIndex                % index refers to page numbers
\CodelineNumbered         % number source lines
\RecordChanges            % record changes
\begin{document}          % main document
  \DocInput{\jobname.dtx} %
  \PrintIndex             %
\end{document}            % end main document
%</driver>
% \fi
%
% ^^A DO NOT TRY TO COMPILE THIS DTX FILE BY ITSELF. IT WILL FAIL.
%
% \section{The \mandistudent{} Package}\setplace{sec:mandib}
%
% \mandi{} comes with an accessory package \mandistudent,
% which extends \mandi{} with a collection of commands physics
% students can use for writing problem solutions. This package 
% focuses on the most frequently needed tools. These commands 
% should always be used in math mode. \mandistudent{} requires, 
% and loads, \mandi{} but \mandi{} doesn't require, and doesn't 
% load, \mandistudent.
%
% Load \mandistudent{} as you would any package in your preamble. 
% There are no package options.
%
%\iffalse
%<*example>
%\fi
\begin{dispListing*}{sidebyside = false,listing only}
  \usepackage{mandistudent}
\end{dispListing*}
%\iffalse
%</example>
%\fi
%
%\iffalse
%<*example>
%\fi
\begin{docCommand}{mandistudentversion}{}
  Typesets the current version and build date.
\end{docCommand}
\begin{dispExample*}{sidebyside = false}
  The version is \mandistudentversion{} and is a stable build.
\end{dispExample*}
%\iffalse
%</example>
%\fi
%
% \subsection{Traditional Vector Notation}
%
%\iffalse
%<*example>
%\fi
\begin{docCommands}[%
    doc updated = 2021-09-18,%
    doc parameter = \marg{symbol}\oarg{labels},%
  ]%
  {%
    {%
      doc name = vec,%
      doc description = use this variant for boldface notation,%
    },%
    {%
      doc name = vec*,%
      doc description = use this variant for arrow notation,%
    }%
  }%
  Powerful and intelligent command for symbolic vector notation. The 
  mandatory argument is the symbol for the vector quantity. The optional 
  label(s) consists of superscripts and/or subscripts and can be 
  mathematical or textual in nature. If textual, be sure to wrap them in 
  |\symup{...}| for proper typesetting. The starred variant gives arrow 
  notation whereas without the star you get boldface notation. Subscript 
  and superscript labels can be arbitrarily mixed, and order doesn't matter.
  This command redefines the default \LaTeXe |\vec| command.
\end{docCommands}
\begin{dispExample*}{lefthand ratio = 0.6}
  \( \vec{p} \)                                 \\
  \( \vec{p}_{2} \)                             \\
  \( \vec{p}^{\symup{ball}} \)                  \\
  \( \vec{p}_{\symup{final}} \)                 \\
  \( \vec{p}^{\symup{ball}}_{\symup{final}} \)  \\
  \( \vec{p}^{\symup{final}}_{\symup{ball}} \)  \\
  \( \vec*{p} \)                                \\
  \( \vec*{p}_{2} \)                            \\
  \( \vec*{p}^{\symup{ball}} \)                 \\
  \( \vec*{p}_{\symup{final}} \)                \\
  \( \vec*{p}^{\symup{ball}}_{\symup{final}} \) \\
  \( \vec*{p}^{\symup{final}}_{\symup{ball}} \)
\end{dispExample*}
%\iffalse
%</example>
%\fi
%
%\iffalse
%<*example>
%\fi
\begin{docCommands}[%
    doc updated = 2021-09-18,%
    doc parameter = \marg{symbol}\oarg{labels},%
  ]%
  {%
    {%
      doc name = dirvec,%
      doc description = use this variant for boldface notation,%
    },%
    {%
      doc name = dirvec*,%
      doc description = use this variant for arrow notation,%
    }%
  }%
  Powerful and intelligent command for typesetting the direction of 
  a vector. The options are the same as those for \refCom{vec}.
\end{docCommands}
\begin{dispExample*}{lefthand ratio = 0.65}
  \( \dirvec{p} \)                                 \\
  \( \dirvec{p}_{2} \)                             \\
  \( \dirvec{p}^{\symup{ball}} \)                  \\
  \( \dirvec{p}_{\symup{final}} \)                 \\
  \( \dirvec{p}^{\symup{ball}}_{\symup{final}} \)  \\
  \( \dirvec{p}^{\symup{final}}_{\symup{ball}} \)  \\
  \( \dirvec*{p} \)                                \\ 
  \( \dirvec*{p}_{2} \)                            \\
  \( \dirvec*{p}^{\symup{ball}} \)                 \\
  \( \dirvec*{p}_{\symup{final}} \)                \\
  \( \dirvec*{p}^{\symup{ball}}_{\symup{final}} \) \\
  \( \dirvec*{p}^{\symup{final}}_{\symup{ball}} \) 
\end{dispExample*}
%\iffalse
%</example>
%\fi
%
%\iffalse
%<*example>
%\fi
\begin{docCommands}
  {%
    {%
      doc name = zerovec,%
      doc description = use this variant for boldface notation,%
    },%
    {%
      doc name = zerovec*,%
      doc description = use this variant for arrow notation,%
    },%
  }%
  Command for typesetting the zero vector. The starred variant gives 
  arrow notation. Without the star you get boldface notation.
\end{docCommands}
\begin{dispExample}
  \( \zerovec \)  \\
  \( \zerovec* \)
\end{dispExample}
%\iffalse
%</example>
%\fi
%
%\iffalse
%<*example>
%\fi
\begin{docCommand}{changein}{}%
  Semantic alias for |\Delta|.
\end{docCommand}
\begin{dispExample}
 \( \changein t \)       \\
 \( \changein \vec{p} \)
\end{dispExample}
%\iffalse
%</example>
%\fi
%
%\iffalse
%<*example>
%\fi
\begin{docCommands}[%
    doc new = 2021-02-21,%
    doc parameter = \oarg{size}\marg{quantity},%
  ]%
  {%
    {%
      doc name = doublebars,%
      doc description = double bars,%
    },%
    {%
      doc name = doublebars*,%
      doc description = double bars for fractions,%
    },%
    {%
      doc name = singlebars,%
      doc description = single bars,%
    },%
    {%
      doc name = singlebars*,%
      doc description = single bars for fractions,%
    },%
    {%
      doc name = anglebrackets,%
      doc description = angle brackets,%
    },%
    {%
      doc name = anglebrackets*,%
      doc description = angle brackets for fractions,%
    },%
    {%
      doc name = parentheses,%
      doc description = parentheses,%
    },%
    {%
      doc name = parentheses*,%
      doc description = parentheses for fractions,%
    },%
    {%
      doc name = squarebrackets,%
      doc description = square brackets,%
    },%
    {%
      doc name = squarebrackets*,%
      doc description = square brackets for fractions,%
    },%
    {%
      doc name = curlybraces,%
      doc description = curly braces,%
    },%
    {%
      doc name = curlybraces*,%
      doc description = curly braces for fractions,%
    },%
  }%
  If no argument is given, a placeholder is provided. 
  Sizers like |\big|,|\Big|,|\bigg|, and |\Bigg| can 
  be optionally specified. Beginners are encouraged 
  not to use them. See the 
  \href{https://www.ctan.org/pkg/mathtools}{\pkg{mathtools}} package 
  documentation for details. 
\end{docCommands}
\begin{dispExample}
  \[ \doublebars{} \]
  \[ \doublebars{\vec{a}} \]
  \[ \doublebars*{\frac{\vec{a}}{3}} \]
  \[ \doublebars[\Bigg]{\frac{\vec{a}}{3}} \]
\end{dispExample}
\begin{dispExample}
  \[ \singlebars{} \]
  \[ \singlebars{x} \]
  \[ \singlebars*{\frac{x}{3}} \]
  \[ \singlebars[\Bigg]{\frac{x}{3}} \]
\end{dispExample}
\begin{dispExample}
  \[ \anglebrackets{} \]
  \[ \anglebrackets{\vec{a}} \]
  \[ \anglebrackets*{\frac{\vec{a}}{3}} \]
  \[ \anglebrackets[\Bigg]{\frac{\vec{a}}{3}} \]
\end{dispExample}
\begin{dispExample}
  \[ \parentheses{} \]
  \[ \parentheses{x} \]
  \[ \parentheses*{\frac{x}{3}} \]
  \[ \parentheses[\Bigg]{\frac{x}{3}} \]
\end{dispExample}
\begin{dispExample}
  \[ \squarebrackets{} \]
  \[ \squarebrackets{x} \]
  \[ \squarebrackets*{\frac{x}{3}} \]
  \[ \squarebrackets[\Bigg]{\frac{x}{3}} \]
\end{dispExample}
\begin{dispExample}
  \[ \curlybraces{} \]
  \[ \curlybraces{x} \]
  \[ \curlybraces*{\frac{x}{3}} \]
  \[ \curlybraces[\Bigg]{\frac{x}{3}} \]
\end{dispExample}
%\iffalse
%</example>
%\fi
%
%\iffalse
%<*example>
%\fi
\begin{docCommands}[%
    doc new = 2021-02-21,%
    doc parameter = \oarg{size}\marg{quantity},%
  ]%
  {%
    {%
      doc name = magnitude,%
      doc description = alias for double bars,%
    },%
    {%
      doc name = magnitude*,%
      doc description = alias for double bars for fractions,%
    },%
    {%
      doc name = norm,%
      doc description = alias for double bars,%
    },%
    {%
      doc name = norm*,%
      doc description = alias for double bars for fractions,%
    },%
    {%
      doc name = absolutevalue,%
      doc description = alias for single bars,%
    },%
    {%
      doc name = absolutevalue*,%
      doc description = alias for single bars for fractions,%
    },%
  }%
  Semantic aliases. Use \refCom{magnitude} or \refCom{magnitude*} to
  typeset the magnitude of a vector.
\end{docCommands}
\begin{dispExample}
  \[ \magnitude{\vec{p}} \]
  \[ \magnitude{\vec*{p}} \]
  \[ \magnitude*{\vec{p}_{\symup{final}}} \]
  \[ \magnitude*{\vec*{p}_{\symup{final}}} \]
\end{dispExample}
%\iffalse
%</example>
%\fi
%
%\iffalse
%<*example>
%\fi
\begin{docCommands}[%
    doc new = 2021-04-06,%
  ]%
  {%
    {%
      doc name = parallelto,%
    },%
    {%
      doc name = perpendicularto,%
    },%
  }%
  Commands for geometric relationships, mainly
  intended for subscripts.
\end{docCommands}
\begin{dispExample*}{lefthand ratio = 0.6}
 \( \vec{F}_{\parallelto} + \vec{F}_{\perpendicularto} \)
\end{dispExample*}
%\iffalse
%</example>
%\fi
%
% \subsection{Problems and Annotated Problem Solutions}
%
%\iffalse
%<*example>
%\fi
\begin{docEnvironments}[%
    doc updated = 2023-08-01,%
    doc parameter = \marg{title},%
    doclang/environment content = problem,%
  ]%
  {%
    {%
      doc name = physicsproblem,%
      doc description = use this variant for vertical lists,%
    },%
    {%
      doc name = physicsproblem*,%
      doc description = use this variant for in-line lists,%
    },%
    {%
      doc name = parts,%
      doc description = provides problem parts,%
    },%
  }%
  Provides an environment for stating physics problems. Each problem will 
  begin on a new page. See the examples for how to handle single and 
  multiple part problems. Due to incompatibilities with \textsc{beamer}
  and the \pkg{enumitem} package, these environments are not defined if
  \textsc{beamer} is loaded.
\end{docEnvironments}
\begin{docCommand}[doc updated = 2023-08-01]{problempart}{}
  Denotes a part of a problem within a \refEnv{parts}
  environment. This command is not defined if \textsc{beamer} 
  is loaded.
\end{docCommand}
\begin{dispExample*}{sidebyside = false}
  \begin{physicsproblem}{Problem 1}
    This is a physics problem with no parts.
  \end{physicsproblem}
\end{dispExample*}
\begin{dispExample*}{sidebyside = false}
  \begin{physicsproblem}{Problem 2}
    This is a physics problem with multiple parts.
    The list is vertical.
    \begin{parts}
      \problempart This is the first part.
      \problempart This is the second part.
      \problempart This is the third part.
    \end{parts}
  \end{physicsproblem}
\end{dispExample*}
\begin{dispExample*}{sidebyside = false}
  \begin{physicsproblem*}{Problem 3}
    This is a physics problem with multiple parts.
    The list is in-line.
    \begin{parts}
      \problempart This is the first part.
      \problempart This is the second part.
      \problempart This is the third part.
    \end{parts}
  \end{physicsproblem*}
\end{dispExample*}
%\iffalse
%</example>
%\fi
%
%\iffalse
%<*example>
%\fi
\begin{docEnvironments}[%
    doc updated = 2021-02-26,%
    doc parameter = {},%
    doclang/environment content = solution steps,%
  ]%
  {%
    {%
      doc name = physicssolution,%
      doc description = use this variant for numbered steps,%
    },%
    {%
      doc name = physicssolution*,%
      doc description = use this variant for unnumbered steps,%
    },%
  }%
  This environment is only for mathematical solutions. The starred 
  variant omits numbering of steps. See the examples.
\end{docEnvironments}
\begin{dispExample}
  \begin{physicssolution}
    x &= y + z \\
    z &= x - y \\
    y &= x - z
  \end{physicssolution}
  \begin{physicssolution*}
    x &= y + z \\
    z &= x - y \\
    y &= x - z
  \end{physicssolution*}
\end{dispExample}
%\iffalse
%</example>
%\fi
%
%\iffalse
%<*example>
%\fi
\begin{docCommand}[doc updated = 2021-02-26]{reason}{\marg{reason}}
  Provides an annotation in a step-by-step solution.
  Keep reasons short and to the point. Wrap mathematical
  content in math mode. 
\end{docCommand}
\begin{dispExample}
  \begin{physicssolution}
    x &= y + z \reason{This is a reason.}     \\
    z &= x - y \reason{This is a reason too.} \\
    y &= x - z \reason{final answer}
  \end{physicssolution}
  \begin{physicssolution*}
    x &= y + z \reason{This is a reason.}     \\
    z &= x - y \reason{This is a reason too.} \\
    y &= x - z \reason{final answer}
  \end{physicssolution*}
\end{dispExample}
%\iffalse
%</example>
%\fi
%
% When writing solutions, remember that the \refEnv{physicssolution} 
% environment is \emph{only} for mathematical content, not textual 
% content or explanations. 
%
%\iffalse
%<*example>
%\fi
\begin{dispListing*}{sidebyside = false,listing only}
  \begin{physicsproblem}{Combined Problem and Solution}
    This is an interesting physics problem.
    \begin{physicssolution}
      The solution goes here.
    \end{physicssolution}
  \end{physicsproblem}
\end{dispListing*}
\begin{dispListing*}{sidebyside = false,listing only}
  \begin{physicsproblem}{Combined Multipart Problem with Solutions}
    This is a physics problem with multiple parts.
    \begin{parts}
      \problempart This is the first part.
        \begin{physicssolution}
          The solution goes here.
        \end{physicssolution}
      \problempart This is the second part.
        \begin{physicssolution}
          The solution goes here.
        \end{physicssolution}
      \problempart This is the third part.
        \begin{physicssolution}
          The solution goes here.
        \end{physicssolution}
    \end{parts}
  \end{physicsproblem}
\end{dispListing*}
%\iffalse
%</example>
%\fi
%
%\iffalse
%<*example>
%\fi
\begin{docCommand}[doc updated = 2023-08-01]{hilite}{%
    \oarg{color}\marg{target}\oarg{shape}
  }%
  Hilites the desired target, which can be an entire mathematical expression 
  or a part thereof. The default color is magenta and the default shape is a
  rectangle. This command is not defined if \textsc{beamer} is loaded.
\end{docCommand} 
\begin{dispListing*}{sidebyside = false,listing only}
  \begin{align*}
    (\Delta s)^2 &= -(\Delta t)^2 + (\Delta x)^2 + (\Delta y)^2 + 
                     (\Delta z)^2 \\
    (\Delta s)^2 &= \hilite{-(\Delta t)^2 + (\Delta x)^2}[rounded rectangle] + 
                     (\Delta y)^2 + (\Delta z)^2 \\
    (\Delta s)^2 &= \hilite{-(\Delta t)^2 + (\Delta x)^2}[rectangle] + 
                     (\Delta y)^2 + (\Delta z)^2 \\
    (\Delta s)^2 &= \hilite{-(\Delta t)^2 + (\Delta x)^2}[ellipse] + 
                     (\Delta y)^2 + (\Delta z)^2 \\
    (\Delta s)^{\hilite{2}[circle]} &= \hilite[green]{-}[circle]
                 (\Delta t)^{\hilite[cyan]{2}[circle]}+
                 (\Delta x)^{\hilite[orange]{2}[circle]} + 
                 (\Delta y)^{\hilite[blue!50]{2}[circle]} +
                 (\Delta z)^{\hilite[violet!45]{2}[circle]}
  \end{align*}
\end{dispListing*}
  \begin{align*}
    (\Delta s)^2 &= -(\Delta t)^2 + (\Delta x)^2 + (\Delta y)^2 + 
                     (\Delta z)^2 \\
    (\Delta s)^2 &= \hilite{-(\Delta t)^2 + (\Delta x)^2}[rounded rectangle] + 
                    (\Delta y)^2 + (\Delta z)^2 \\
    (\Delta s)^2 &= \hilite{-(\Delta t)^2 + (\Delta x)^2}[rectangle] + 
                    (\Delta y)^2 + (\Delta z)^2 \\
    (\Delta s)^2 &= \hilite{-(\Delta t)^2 + (\Delta x)^2}[ellipse] + 
                    (\Delta y)^2 + (\Delta z)^2 \\
    (\Delta s)^{\hilite{2}[circle]} &= \hilite[green]{-}[circle]
                    (\Delta t)^{\hilite[cyan]{2}[circle]}+
                    (\Delta x)^{\hilite[orange]{2}[circle]} + 
                    (\Delta y)^{\hilite[blue!50]{2}[circle]} +
                    (\Delta z)^{\hilite[violet!45]{2}[circle]}
  \end{align*}
\begin{dispListing*}{sidebyside = false,listing only}
  \begin{align*}
    \Delta\vec{p} &= \vec{F}_{\sumup{net}}\Delta t \\
    \hilite[orange]{\Delta\vec{p}}[circle] &= \vec{F}_{\symup{net}}\Delta t \\
    \Delta\vec{p} &= \hilite[yellow!50]{\vec{F}_{\symup{net}}}
                     [rounded rectangle]\Delta t \\
    \Delta\vec{p} &= \vec{F}_{\symup{net}}\hilite[olive!50]
                     {\Delta t}[rectangle] \\
    \Delta\vec{p} &= \hilite[cyan!50]{\vec{F}_{\symup{net}}\Delta t}
                     [ellipse] \\
    \hilite{\Delta\vec{p}}[rectangle] &= \vec{F}_{\symup{net}}\Delta t
  \end{align*}
\end{dispListing*}
  \begin{align*}
    \Delta\vec{p} &= \vec{F}_{\symup{net}}\Delta t \\
    \hilite[orange]{\Delta\vec{p}}[circle] &= \vec{F}_{\symup{net}}
                     \Delta t \\
    \Delta\vec{p} &= \hilite[yellow!50]{\vec{F}_{\symup{net}}}
                     [rounded rectangle]\Delta t \\
    \Delta\vec{p} &= \vec{F}_{\symup{net}}\hilite[olive!50]{\Delta t}
                     [rectangle] \\
    \Delta\vec{p} &= \hilite[cyan!50]{\vec{F}_{\symup{net}}\Delta t}
                     [ellipse] \\
    \hilite{\Delta\vec{p}}[rectangle] &= \vec{F}_{\symup{net}}\Delta t
  \end{align*}
%\iffalse
%</example>
%\fi
%
%\iffalse
%<*example>
%\fi
\begin{docCommand}[doc updated = 2021-09-18]{image}{%
    \oarg{options}\marg{caption}\marg{label}\marg{image}
  }%
  Simplified interface for importing an image. The images are treated 
  as floats, so they may not appear at the most logically intuitive 
  place.
\end{docCommand}
\begin{dispListing*}{sidebyside = false,listing only,verbatim ignore percent}
  \image[scale=0.20]{example-image-1x1}
    {Image shown 20 percent actual size.}{reffig1}
\end{dispListing*}
\image[scale=0.20]{example-image-1x1}
    {Image shown 20 percent actual size.}{reffig1}
\begin{dispExample*}{sidebyside=false}
  Figure \ref{reffig1} is nice. 
  It's captioned \nameref{reffig1} and is on page \pageref{reffig1}.
\end{dispExample*}
\begin{dispListing*}{sidebyside = false,listing only,verbatim ignore percent}
  \image[scale=0.20,angle=45]{example-image-1x1}
  {Image shown 20 percent actual size and rotated.}{reffig1}
\end{dispListing*}
\image[scale=0.20,angle=45]{example-image-1x1}
{Image shown 20 percent actual size and rotated.}{reffig2}
\begin{dispExample*}{sidebyside = false}
  Figure \ref{reffig2} is nice. 
  It's captioned \nameref{reffig2} and is on page \pageref{reffig2}.
\end{dispExample*}
%\iffalse
%</example>
%\fi
%
% \subsection{Coordinate-Free and Index Notation}
%
% Beyond the current level of introductory physics, we need intelligent 
% commands for typesetting vector and tensor symbols and components 
% suitable for both coordinate-free and index notations.
%
%\iffalse
%<*example>
%\fi
\begin{docCommands}[%
    doc parameter = \oarg{delimiter}\marg{\( c_{1},\dots,c_{n} \)},%
  ]%
  {%
    {%
      doc name = colvec,%
    },%
    {%
      doc name = rowvec,%
    },%
  }%
  Typesets column vectors and row vectors as numeric or symbolic components. 
  There can be more than three components. The delimiter used in the list of 
  components can be specified; the default is a comma. Units are not 
  supported, so these are mainly for symbolic work.
\end{docCommands}
\begin{dispExample}
  \[ \colvec{1,2,3} \]
  \[ \rowvec{1,2,3} \]
  \[ \colvec{x^{0},x^{1},x^{2},x^{3}} \]
  \[ \rowvec{x_{0},x_{1},x_{2},x_{3}} \]
\end{dispExample}
%\iffalse
%</example>
%\fi
%
%\iffalse
%<*example>
%\fi
\begin{docCommands}[%
    doc parameter = \marg{symbol},%
  ]%
  {%
    {%
      doc name = veccomp,%
      doc description = use this variant for coordinate-free vector notation,%
    },%
    {%
      doc name = veccomp*,%
      doc description = use this variant for index vector notation,%
    },%
    {%
      doc name = tencomp,%
      doc description = use this variant for coordinate-free tensor notation,%
    },%
    {%
      doc name = tencomp*,%
      doc description = use this variant for index tensor notation,%
    },%
  }%
  Conforms to ISO 80000-2 notation.
\end{docCommands}
\begin{dispExample}
  \( \veccomp{r} \)  \\
  \( \veccomp*{r} \) \\
  \( \tencomp{r} \)  \\
  \( \tencomp*{r} \)
\end{dispExample}
%\iffalse
%</example>
%\fi
%
%\iffalse
%<*example>
%\fi
\begin{docCommands}[%
  doc parameter = \marg{index}\marg{index},%
  ]%
  {%
    {%
      doc name = valence,%
    },%
    {%
      doc name = valence*,%
    },%
  }%
  Typesets tensor valence. The starred variant typesets it horizontally.
\end{docCommands}
\begin{dispExample}
  A vector is a \( \valence{1}{0} \) tensor.  \\
  A vector is a \( \valence*{1}{0} \) tensor.
\end{dispExample}
%\iffalse
%</example>
%\fi
%
%\iffalse
%<*example>
%\fi
\begin{docCommands}[%
  doc parameter = \marg{slot,slot},%
  ]%
  {%
    {%
      doc name = contraction,%
    },%
    {%
      doc name = contraction*,%
    },%
  }%
  Typesets tensor contraction in coordinate-free notation. There
  is no standard for this so we assert one here.
\end{docCommands}
\begin{dispExample}
  \( \contraction{1,2} \)  \\
  \( \contraction*{1,2} \)
\end{dispExample}
%\iffalse
%</example>
%\fi
%
%\iffalse
%<*example>
%\fi
\begin{docCommands}%
  {%
    {%
      doc name = slot,%
      doc parameter = \oarg{vector},%
    },%
    {%
      doc name = slot*,%
      doc parameter = \oarg{vector},%
    },%
  }%
  An intelligent slot command for coordinate-free vector
  and tensor notation. The starred variants suppress the 
  underscore.
\end{docCommands}
\begin{dispExample}
  \( (\slot) \)          \\
  \( (\slot[\vec{a}]) \) \\
  \( (\slot*) \)         \\
  \( (\slot*[\vec{a}]) \)
\end{dispExample}
%\iffalse
%</example>
%\fi
%
% \changes{v3.1.0}{2022-01-27}
%   {\linktoplace{sec:mandib}{mandistudent} \cs{diff} renamed to 
%    \refCom{df} for compatibility with the \pkg{numerica} package}
%\iffalse
%<*example>
%\fi
\begin{docCommand}[doc updated = 2022-01-27]{df}{}%
  Intelligent differential and exterior derivative
  operator.
\end{docCommand}
\begin{dispExample}
 \[ 
   \int x\,dx 
 \]
 \[
   \int x\,\df{x}
 \]
 \[
   \int x\,\df*{x} 
 \]
\end{dispExample}
%\iffalse
%</example>
%\fi
%
% \changes{v3.1.0}{2022-01-27}
%   {\linktoplace{sec:mandib}{mandistudent} All instances of 
%    \texttt{GlowScript} have been changed to \texttt{Web VPython}}
% \subsection{\WebVPython{} and \VPython{} Program Listings}
%
% \href{https://\gsurl}{\WebVPython}
% \footnote{On November 9, 2021 \texttt{GlowScript} was renamed to 
% \WebVPython. The website was changed to
% \href{https://\gsurl}{https://\gsurl}.}
% and 
% \href{https://\vpurl}{VPython}\footnote{\href{https://\vpurl}{https://\vpurl}} 
% are programming environments (both use \href{https://www.python.org}{Python})
% frequently used in introductory physics to introduce students
% for modeling physical systems. \mandi{} makes including code listings
% very simple for students.
%
% \subsection{The \texttt{\small webvpythonblock} Environment}
%
%\iffalse
%<*example>
%\fi
\begin{docEnvironments}[%
    doc parameter = {\oarg{options}(\meta{link})\marg{caption}},% 
    doclang/environment content=WebVPython code,%
  ]%
  {%
    {%
      doc updated = 2023-08-01,%
      doc name = webvpythonblock,%    
      doc description = now includes a QR code,%
    },%
    {%
      doc updated = 2023-08-01,%
      doc name = webvpythonblock*,%
      doc description = use this variant to omit QR code,%
    },%
  }%
  Code placed here is nicely formatted and optionally linked to its source on 
  \href{https://\gsurl}{\WebVPythonorg}, which must be in a public (not private)
  folder. Clicking anywhere in the code window (between the black horizontal bars) 
  or on the URL will open the link in the default browser. A caption is mandatory, 
  and a label is internally generated. The listing always begins on a new page. 
  A URL shortening utility is recommended to keep the URL from getting unruly
  especially if it generates an overfull box error. For convenience, |https://| 
  is automatically prepended to the URL and can be omitted. The |#| character 
  in a URL should not cause problems. The default URL is that of the \WebVPython{} 
  home page.
\end{docEnvironments}
\begin{dispListing*}{sidebyside = false}
\begin{webvpythonblock}
  (glowscript.org/#/user/heafnerj/folder/mandidemo/program/mandidemo)
  {Example With QR Code}
Web VPython 3.2

scene.width = 400
scene.height = 760
# constants and data
g = 9.8       # m/s^2
mball = 0.03  # kg
Lo = 0.26     # m
ks = 1.8      # N/m
deltat = 0.01 # s

# objects (origin is at ceiling)
ceiling = box(pos=vector(0,0,0), length=0.2, height=0.01, 
              width=0.2)
ball = sphere(pos=vector(0,-0.3,0),radius=0.025,
              color=color.orange)
spring = helix(pos=ceiling.pos, axis=ball.pos-ceiling.pos,
               color=color.cyan,thickness=0.003,coils=40,
               radius=0.010)

# initial values
pball = mball * vector(0,0,0)      # kg m/s
Fgrav = mball * g * vector(0,-1,0) # N
t = 0

# improve the display
scene.autoscale = False        # turn off automatic camera zoom
scene.center = vector(0,-Lo,0) # move camera down
scene.waitfor('click')         # wait for a mouse click

# initial calculation loop
# calculation loop
while t < 10:
    rate(100)
    # we need the stretch
    s = mag(ball.pos) - Lo
    # we need the spring force
    Fspring = ks * s * -norm(spring.axis)
    Fnet = Fgrav + Fspring
    pball = pball + Fnet * deltat
    ball.pos = ball.pos + (pball / mball) * deltat
    spring.axis = ball.pos - ceiling.pos
    t = t + deltat
\end{webvpythonblock}
\end{dispListing*}
\begin{webvpythonblock}
  (glowscript.org/#/user/heafnerj/folder/mandidemo/program/mandidemo)
  {Example With QR Code}
Web VPython 3.2

scene.width = 400
scene.height = 760
# constants and data
g = 9.8       # m/s^2
mball = 0.03  # kg
Lo = 0.26     # m
ks = 1.8      # N/m
deltat = 0.01 # s

# objects (origin is at ceiling)
ceiling = box(pos=vector(0,0,0), length=0.2, height=0.01, 
              width=0.2)
ball = sphere(pos=vector(0,-0.3,0),radius=0.025,
              color=color.orange)
spring = helix(pos=ceiling.pos, axis=ball.pos-ceiling.pos,
               color=color.cyan,thickness=0.003,coils=40,
               radius=0.010)

# initial values
pball = mball * vector(0,0,0)      # kg m/s
Fgrav = mball * g * vector(0,-1,0) # N
t = 0

# improve the display
scene.autoscale = False        # turn off automatic camera zoom
scene.center = vector(0,-Lo,0) # move camera down
scene.waitfor('click')         # wait for a mouse click

# initial calculation loop
# calculation loop
while t < 10:
    rate(100)
    # we need the stretch
    s = mag(ball.pos) - Lo
    # we need the spring force
    Fspring = ks * s * -norm(spring.axis)
    Fnet = Fgrav + Fspring
    pball = pball + Fnet * deltat
    ball.pos = ball.pos + (pball / mball) * deltat
    spring.axis = ball.pos - ceiling.pos
    t = t + deltat
\end{webvpythonblock}
%  
%\iffalse
%</example>
%\fi
%
% \newpage
% Here is how one would reference this program elsewhere. Notice the 
% references are numbered sequentially within the document.
%
%\iffalse
%<*example>
%\fi
\begin{dispExample*}{sidebyside = false}
  \WebVPython{} program \ref{gs:1} is nice. 
  It's called \nameref{gs:1} and is on page \pageref{gs:1}.
\end{dispExample*}
%\iffalse
%</example>
%\fi
%
% \newpage
%
%\iffalse
%<*example>
%\fi
\begin{dispListing*}{sidebyside = false}
\begin{webvpythonblock*}
  (glowscript.org/#/user/heafnerj/folder/mandidemo/program/mandidemo)
  {Example Without QR Code}
Web VPython 3.2

scene.width = 400
scene.height = 760
# constants and data
g = 9.8       # m/s^2
mball = 0.03  # kg
Lo = 0.26     # m
ks = 1.8      # N/m
deltat = 0.01 # s

# objects (origin is at ceiling)
ceiling = box(pos=vector(0,0,0), length=0.2, height=0.01, 
              width=0.2)
ball = sphere(pos=vector(0,-0.3,0),radius=0.025,
              color=color.orange)
spring = helix(pos=ceiling.pos, axis=ball.pos-ceiling.pos,
               color=color.cyan,thickness=0.003,coils=40,
               radius=0.010)

# initial values
pball = mball * vector(0,0,0)      # kg m/s
Fgrav = mball * g * vector(0,-1,0) # N
t = 0

# improve the display
scene.autoscale = False        # turn off automatic camera zoom
scene.center = vector(0,-Lo,0) # move camera down
scene.waitfor('click')         # wait for a mouse click

# initial calculation loop
# calculation loop
while t < 10:
    rate(100)
    # we need the stretch
    s = mag(ball.pos) - Lo
    # we need the spring force
    Fspring = ks * s * -norm(spring.axis)
    Fnet = Fgrav + Fspring
    pball = pball + Fnet * deltat
    ball.pos = ball.pos + (pball / mball) * deltat
    spring.axis = ball.pos - ceiling.pos
    t = t + deltat
\end{webvpythonblock*}
\end{dispListing*}
\begin{webvpythonblock*}
  (glowscript.org/#/user/heafnerj/folder/mandidemo/program/mandidemo)
  {Example Without QR Code}
Web VPython 3.2

scene.width = 400
scene.height = 760
# constants and data
g = 9.8       # m/s^2
mball = 0.03  # kg
Lo = 0.26     # m
ks = 1.8      # N/m
deltat = 0.01 # s

# objects (origin is at ceiling)
ceiling = box(pos=vector(0,0,0), length=0.2, height=0.01, 
              width=0.2)
ball = sphere(pos=vector(0,-0.3,0),radius=0.025,
              color=color.orange)
spring = helix(pos=ceiling.pos, axis=ball.pos-ceiling.pos,
               color=color.cyan,thickness=0.003,coils=40,
               radius=0.010)

# initial values
pball = mball * vector(0,0,0)      # kg m/s
Fgrav = mball * g * vector(0,-1,0) # N
t = 0

# improve the display
scene.autoscale = False        # turn off automatic camera zoom
scene.center = vector(0,-Lo,0) # move camera down
scene.waitfor('click')         # wait for a mouse click

# initial calculation loop
# calculation loop
while t < 10:
    rate(100)
    # we need the stretch
    s = mag(ball.pos) - Lo
    # we need the spring force
    Fspring = ks * s * -norm(spring.axis)
    Fnet = Fgrav + Fspring
    pball = pball + Fnet * deltat
    ball.pos = ball.pos + (pball / mball) * deltat
    spring.axis = ball.pos - ceiling.pos
    t = t + deltat
\end{webvpythonblock*}
\begin{dispExample*}{sidebyside = false}
  \WebVPython{} program \ref{gs:2} is nice. 
  It's called \nameref{gs:2} and is on page \pageref{gs:2}.
\end{dispExample*}
%  
%\iffalse
%</example>
%\fi
%
% \subsection{The \texttt{\small vpythonfile} Command}
%
%\iffalse
%<*example>
%\fi
\begin{docCommand}[doc updated = 2023-08-01]{vpythonfile}
  {\oarg{options}(\meta{link})\marg{file}\marg{caption}}
  Command to load and typeset a \VPython{} program, read from local file
  \marg{file}. Clicking anywhere in the code window (between the black 
  horizontal bars) or on the URL will open the link in the default browser. 
  A caption is mandatory,  and a label is internally generated. The listing 
  always begins on a new page. A URL shortening utility is recommended to 
  keep the URL from getting unruly especially if it generates an overfull 
  box error. For convenience, |https://| is automatically prepended to the 
  URL and can be omitted. The default URL is that of the \VPython{} home page.
\end{docCommand}
\begin{dispListing*}{sidebyside = false}
\vpythonfile{vdemo.py}{A \VPython{} Program}
\end{dispListing*}
\vpythonfile{vdemo.py}{A \VPython{} Program}
%\iffalse
%</example>
%\fi
%
%\iffalse
%<*example>
%\fi
\begin{dispExample*}{sidebyside = false}
  \VPython{} program \ref{vp:1} is nice. 
  It's called \nameref{vp:1} and is on page \pageref{vp:1}.
\end{dispExample*}
%  
%\iffalse
%</example>
%\fi
%
% \subsection{The \texttt{\small webvpythoninline} and 
%   \texttt{\small vpythoninline} Commands}
%
%\iffalse
%<*example>
%\fi
\begin{docCommands}[%
    doc updated = 2021-02-26,%
  ]%
  {%
    {%
      doc name = webvpythoninline,%
      doc parameter = \marg{Web VPython code},%
    },%
    {%
      doc name = vpythoninline,%
      doc parameter = \marg{VPython code},%
    },%
  }%
  Typesets a small, in-line snippet of code. The snippet should be
  less than one line long.
\end{docCommands}
\begin{dispExample*}{sidebyside = false}
  \WebVPython{} programs begin with \webvpythoninline{Web VPython 3.2}. 
\end{dispExample*}
\begin{dispExample*}{sidebyside = false}
  \VPython{} programs begin with \vpythoninline{from vpython import *}. 
\end{dispExample*}
%\iffalse
%</example>
%\fi
%
% \MaybeStop{}
%
% \newgeometry{left=0.50in,right=0.50in,top=1.00in,bottom=1.00in}
% \subsection{\mandistudent{} Source Code}
%
% \iffalse
%<*package>
% \fi
% Definine the package version and date for global use, exploiting the fact
% that in a \pkg{.sty} file there is now no need for |\makeatletter| and
% |\makeatother|. This simplifies defining internal commands, with |@| 
% in the name, that are not for the user to know about.
%
% \changes{v3.1.0}{2022-01-27}
%   {\linktoplace{sec:mandib}{mandistudent} Version number works}
%    \begin{macrocode}
\def\mandistudent@version{3.2.2}
\def\mandistudent@date{2024-02-27}
\NeedsTeXFormat{LaTeX2e}[2020-02-02]
\DeclareRelease{v3.2.2}{2024-02-27}{mandistudent.sty}
\DeclareCurrentRelease{v\mandistudent@version}{\mandistudent@date}
\ProvidesPackage{mandistudent}
  [\mandistudent@date\space v\mandistudent@version\space Macros for introductory physics]
%    \end{macrocode}
%
% Define a convenient package version command.
%
%    \begin{macrocode}
\newcommand*{\mandistudentversion}{v\mandistudent@version\space dated \mandistudent@date}
%    \end{macrocode}
%
% Load third party packages, documenting why each one is needed.
%
% AMS goodness. Don't load \pkg{amssymb} or \pkg{amsfonts}.
%
%    \begin{macrocode}
\RequirePackage{amsmath}
%    \end{macrocode}
%
% We need \pkg{enumitem} for the |physicsproblem| environment.
%
% \textsc{beamer} is not compatible with \pkg{enumitem} so if
% \textsc{beamer} is loaded certain commands are not defined.
%
%    \begin{macrocode}
\IfClassLoadedTF{beamer}%
  {}%
  {%
    \RequirePackage[inline]{enumitem}%
  }%
%    \end{macrocode}
%
% We need \pkg{eso-pic} for |\hilite|.
%
%    \begin{macrocode}
\RequirePackage{eso-pic}
%    \end{macrocode}
%
% We need \pkg{esvect} for nice vector arrows, style g.
%
%    \begin{macrocode}
\RequirePackage[g]{esvect}
%    \end{macrocode}
%
% We need \pkg{pgfopts} for a key-value interface.
%
%    \begin{macrocode}
\RequirePackage{pgfopts}
%    \end{macrocode}
%
% We need \pkg{iftex} so we can require \lualatex.
%
%    \begin{macrocode}
\RequirePackage{iftex}
%    \end{macrocode}
%
% We need \pkg{makebox} for consistent |\dirvect| notation.
%
%    \begin{macrocode}
\RequirePackage{makebox}
%    \end{macrocode}
%
% We need \mandi{} to load \pkg{mathtools} and \pkg{unicode-math}.
%
%    \begin{macrocode}
\IfPackageLoadedTF{mandi}%
  {}%
  {%
    \RequirePackage{mandi}%
  }%
%    \end{macrocode}
%
% We need \pkg{nicematrix} for column and row vectors.
%
%    \begin{macrocode}
\RequirePackage{nicematrix}
%    \end{macrocode}
%
% We need \pkg{qrcode} for QR codes in |webvpythonblock|.
%
%    \begin{macrocode}
\RequirePackage{qrcode}
%    \end{macrocode}
%
% Set the default size for QR codes.
%
%    \begin{macrocode}
\qrset{height=1.5cm}
%    \end{macrocode}
%
% We need \pkg{tcolorbox} for program listings.
%
%    \begin{macrocode}
\RequirePackage[most]{tcolorbox}
%    \end{macrocode}
%
% We need \pkg{tensor} for index notation.
%
%    \begin{macrocode}
\RequirePackage{tensor}
%    \end{macrocode}
%    \begin{macrocode}
%
% We need \pkg{tikz} for |\hilite|..
%
\RequirePackage{tikz}
\usetikzlibrary{shapes,fit,tikzmark}
%    \end{macrocode}
%
% Load \pkg{xparse} if necessary.
%
%    \begin{macrocode}
\IfFormatAtLeastTF{2020-10-01}
  {}%
  {\RequirePackage{xparse}}%
%    \end{macrocode}
%
% Always load \pkg{hyperref} last if possible.
%
%    \begin{macrocode}
\RequirePackage{hyperref}
%    \end{macrocode}
%
% We require the \lualatex{} engine.
%
%    \begin{macrocode}
\RequireLuaTeX
%    \end{macrocode}
%
% Set up the fonts to be consistent with ISO 80000-2 notation.
% The \href{https://www.ctan.org/pkg/unicode-math}{\pkg{unicode-math}} package 
% loads the \href{https://www.ctan.org/pkg/fontspec}{\pkg{fontspec}} and 
% \href{https://www.ctan.org/pkg/xparse}{\pkg{xparse}}
% packages. Note that \pkg{xparse} is now part of the \LaTeXe{} kernel.
% Because \pkg{unicode-math} is required, all documents using \mandi{} must
% be compiled with an engine that supports Unicode, and I recommend \lualatex.
%
%    \begin{macrocode}
\unimathsetup{math-style=ISO}
%    \end{macrocode}
%
% Use normal math letters from Latin Modern Math for familiarity with 
% textbooks. This gives a better J.
%
%    \begin{macrocode}
\setmathfont[Scale=MatchLowercase]
  {Latin Modern Math}
%    \end{macrocode}
%
% Borrow from TeX Gyre DejaVu Math for vectors and tensors to get 
% single-storey lowercase g.
%
%    \begin{macrocode}
\setmathfont[Scale=MatchLowercase,range={sfit/{latin},bfsfit/{latin}}]
  {TeX Gyre DejaVu Math}
%    \end{macrocode}
%
% Borrow from TeX Gyre DejaVu Math to get single-storey lowercase g.
%
%    \begin{macrocode}
\setmathfont[Scale=MatchLowercase,range={sfup/{latin},bfsfup/{latin}}]
  {TeX Gyre DejaVu Math}
%    \end{macrocode}
%
% Borrow |mathscr| and |mathbfscr| from XITS Math.\newline
% See \url{https://tex.stackexchange.com/a/120073/218142}.
%
%    \begin{macrocode}
\setmathfont[Scale=MatchLowercase,range={\mathscr,\mathbfscr}]{XITS Math}
%    \end{macrocode}
%
% Get original and bold |mathcal| fonts.\newline
% See \url{https://tex.stackexchange.com/a/21742/218142}.
%
%    \begin{macrocode}
\setmathfont[Scale=MatchLowercase,range={\mathcal,\mathbfcal},StylisticSet=1]{XITS Math}
%    \end{macrocode}
%
% Borrow Greek |sfup| and |sfit| letters from STIX Two Math.
% Since this isn't officially supported in \pkg{unicode-math}
% we have to manually set this up.
%
%    \begin{macrocode}
\setmathfont[Scale=MatchLowercase,range={"E17C-"E1F6}]{STIX Two Math}
\newfontfamily{\symsfgreek}{STIX Two Math}
%    \end{macrocode}
%
% I don't understand why |\text{...}| is necessary.
%
%    \begin{macrocode}
\newcommand{\symsfupalpha}      {\text{\symsfgreek{^^^^e196}}}
\newcommand{\symsfupbeta}       {\text{\symsfgreek{^^^^e197}}}
\newcommand{\symsfupgamma}      {\text{\symsfgreek{^^^^e198}}}
\newcommand{\symsfupdelta}      {\text{\symsfgreek{^^^^e199}}}
\newcommand{\symsfupepsilon}    {\text{\symsfgreek{^^^^e1af}}}
\newcommand{\symsfupvarepsilon} {\text{\symsfgreek{^^^^e19a}}}
\newcommand{\symsfupzeta}       {\text{\symsfgreek{^^^^e19b}}}
\newcommand{\symsfupeta}        {\text{\symsfgreek{^^^^e19c}}}
\newcommand{\symsfuptheta}      {\text{\symsfgreek{^^^^e19d}}}
\newcommand{\symsfupvartheta}   {\text{\symsfgreek{^^^^e1b0}}}
\newcommand{\symsfupiota}       {\text{\symsfgreek{^^^^e19e}}}
\newcommand{\symsfupkappa}      {\text{\symsfgreek{^^^^e19f}}}
\newcommand{\symsfuplambda}     {\text{\symsfgreek{^^^^e1a0}}}
\newcommand{\symsfupmu}         {\text{\symsfgreek{^^^^e1a1}}}
\newcommand{\symsfupnu}         {\text{\symsfgreek{^^^^e1a2}}}
\newcommand{\symsfupxi}         {\text{\symsfgreek{^^^^e1a3}}}
\newcommand{\symsfupomicron}    {\text{\symsfgreek{^^^^e1a4}}}
\newcommand{\symsfuppi}         {\text{\symsfgreek{^^^^e1a5}}}
\newcommand{\symsfupvarpi}      {\text{\symsfgreek{^^^^e1b3}}}
\newcommand{\symsfuprho}        {\text{\symsfgreek{^^^^e1a6}}}
\newcommand{\symsfupvarrho}     {\text{\symsfgreek{^^^^e1b2}}}
\newcommand{\symsfupsigma}      {\text{\symsfgreek{^^^^e1a8}}}
\newcommand{\symsfupvarsigma}   {\text{\symsfgreek{^^^^e1a7}}}
\newcommand{\symsfuptau}        {\text{\symsfgreek{^^^^e1a9}}}
\newcommand{\symsfupupsilon}    {\text{\symsfgreek{^^^^e1aa}}}
\newcommand{\symsfupphi}        {\text{\symsfgreek{^^^^e1b1}}}
\newcommand{\symsfupvarphi}     {\text{\symsfgreek{^^^^e1ab}}}
\newcommand{\symsfupchi}        {\text{\symsfgreek{^^^^e1ac}}}
\newcommand{\symsfuppsi}        {\text{\symsfgreek{^^^^e1ad}}}
\newcommand{\symsfupomega}      {\text{\symsfgreek{^^^^e1ae}}}
\newcommand{\symsfupDelta}      {\text{\symsfgreek{^^^^e180}}}
\newcommand{\symsfupGamma}      {\text{\symsfgreek{^^^^e17f}}}
\newcommand{\symsfupTheta}      {\text{\symsfgreek{^^^^e18e}}}
\newcommand{\symsfupLambda}     {\text{\symsfgreek{^^^^e187}}}
\newcommand{\symsfupXi}         {\text{\symsfgreek{^^^^e18a}}}
\newcommand{\symsfupPi}         {\text{\symsfgreek{^^^^e18c}}}
\newcommand{\symsfupSigma}      {\text{\symsfgreek{^^^^e18f}}}
\newcommand{\symsfupUpsilon}    {\text{\symsfgreek{^^^^e191}}}
\newcommand{\symsfupPhi}        {\text{\symsfgreek{^^^^e192}}}
\newcommand{\symsfupPsi}        {\text{\symsfgreek{^^^^e194}}}
\newcommand{\symsfupOmega}      {\text{\symsfgreek{^^^^e195}}}
\newcommand{\symsfitalpha}      {\text{\symsfgreek{^^^^e1d8}}}
\newcommand{\symsfitbeta}       {\text{\symsfgreek{^^^^e1d9}}}
\newcommand{\symsfitgamma}      {\text{\symsfgreek{^^^^e1da}}}
\newcommand{\symsfitdelta}      {\text{\symsfgreek{^^^^e1db}}}
\newcommand{\symsfitepsilon}    {\text{\symsfgreek{^^^^e1f1}}}
\newcommand{\symsfitvarepsilon} {\text{\symsfgreek{^^^^e1dc}}}
\newcommand{\symsfitzeta}       {\text{\symsfgreek{^^^^e1dd}}}
\newcommand{\symsfiteta}        {\text{\symsfgreek{^^^^e1de}}}
\newcommand{\symsfittheta}      {\text{\symsfgreek{^^^^e1df}}}
\newcommand{\symsfitvartheta}   {\text{\symsfgreek{^^^^e1f2}}}
\newcommand{\symsfitiota}       {\text{\symsfgreek{^^^^e1e0}}}
\newcommand{\symsfitkappa}      {\text{\symsfgreek{^^^^e1e1}}}
\newcommand{\symsfitlambda}     {\text{\symsfgreek{^^^^e1e2}}}
\newcommand{\symsfitmu}         {\text{\symsfgreek{^^^^e1e3}}}
\newcommand{\symsfitnu}         {\text{\symsfgreek{^^^^e1e4}}}
\newcommand{\symsfitxi}         {\text{\symsfgreek{^^^^e1e5}}}
\newcommand{\symsfitomicron}    {\text{\symsfgreek{^^^^e1e6}}}
\newcommand{\symsfitpi}         {\text{\symsfgreek{^^^^e1e7}}}
\newcommand{\symsfitvarpi}      {\text{\symsfgreek{^^^^e1f5}}}
\newcommand{\symsfitrho}        {\text{\symsfgreek{^^^^e1e8}}}
\newcommand{\symsfitvarrho}     {\text{\symsfgreek{^^^^e1f4}}}
\newcommand{\symsfitsigma}      {\text{\symsfgreek{^^^^e1ea}}}
\newcommand{\symsfitvarsigma}   {\text{\symsfgreek{^^^^e1e9}}}
\newcommand{\symsfittau}        {\text{\symsfgreek{^^^^e1eb}}}
\newcommand{\symsfitupsilon}    {\text{\symsfgreek{^^^^e1ec}}}
\newcommand{\symsfitphi}        {\text{\symsfgreek{^^^^e1f3}}}
\newcommand{\symsfitvarphi}     {\text{\symsfgreek{^^^^e1ed}}}
\newcommand{\symsfitchi}        {\text{\symsfgreek{^^^^e1ee}}}
\newcommand{\symsfitpsi}        {\text{\symsfgreek{^^^^e1ef}}}
\newcommand{\symsfitomega}      {\text{\symsfgreek{^^^^e1f0}}}
\newcommand{\symsfitDelta}      {\text{\symsfgreek{^^^^e1c2}}}
\newcommand{\symsfitGamma}      {\text{\symsfgreek{^^^^e1c1}}}
\newcommand{\symsfitTheta}      {\text{\symsfgreek{^^^^e1d0}}}
\newcommand{\symsfitLambda}     {\text{\symsfgreek{^^^^e1c9}}}
\newcommand{\symsfitXi}         {\text{\symsfgreek{^^^^e1cc}}}
\newcommand{\symsfitPi}         {\text{\symsfgreek{^^^^e1ce}}}
\newcommand{\symsfitSigma}      {\text{\symsfgreek{^^^^e1d1}}}
\newcommand{\symsfitUpsilon}    {\text{\symsfgreek{^^^^e1d3}}}
\newcommand{\symsfitPhi}        {\text{\symsfgreek{^^^^e1d4}}}
\newcommand{\symsfitPsi}        {\text{\symsfgreek{^^^^e1d6}}}
\newcommand{\symsfitOmega}      {\text{\symsfgreek{^^^^e1d7}}}
%    \end{macrocode}
%
% Tweak the \href{https://www.ctan.org/pkg/esvect}{\pkg{esvect}} package 
% fonts to get the correct font size.\newline
% See \url{https://tex.stackexchange.com/a/566676}.
%
%    \begin{macrocode}
\DeclareFontFamily{U}{esvect}{}
\DeclareFontShape{U}{esvect}{m}{n}{%
  <-5.5> vect5
  <5.5-6.5> vect6
  <6.5-7.5> vect7
  <7.5-8.5> vect8
  <8.5-9.5> vect9
  <9.5-> vect10
}{}%
%    \end{macrocode}
%
% Write a banner to the console showing the options in use.
%
%    \begin{macrocode}
\typeout{}%
\typeout{mandistudent: You are using mandistudent \mandistudentversion.}%
\typeout{mandistudent: This package requires LuaLaTeX.}%
\typeout{mandistudent: This package changes the default math font(s).}%
\typeout{mandistudent: This package redefines the \protect\vec\space command.}%
\IfClassLoadedTF{beamer}%
  {%
    \typeout{mandistudent: BEAMER detected. Certain commands will not be defined.}%
  }%
  {}%
\typeout{}%
%    \end{macrocode}
%
% A better, intelligent coordinate-free \refCom{vec} command. Note the use of 
% the |e{_^}| type of optional argument. This accounts for much of the 
% flexibility and power of this command. Also note the use of the \TeX{} 
% primitives |\sb{}| and |\sp{}|. Why doesn't it work when I put spaces 
% around |#3| or |#4|? Because outside of |\ExplSyntaxOn...\ExplSyntaxOff|, 
% the |_| character has a different catcode and is treated as a mathematical 
% entity.\newline
% See \url{https://tex.stackexchange.com/q/554706/218142}.\newline
% See also \url{https://tex.stackexchange.com/a/531037/218142}.
%
% \changes{v3.1.0}{2022-01-27}
%   {\linktoplace{sec:mandib}{mandistudent} \refCom{vec} and \refCom{dirvec}%
%    no longer add \cs{scriptspace} when no sub/superscript is given}
%    \begin{macrocode}
\RenewDocumentCommand{\vec}{ s m e{_^} }%
  {%
    \IfBooleanTF{#1}
      {%
        \vv{#2}%
        \IfValueT{#4}%
          {\sp{\,#4\vphantom{\smash[t]{\big|}}}}
      }%
      {%
        \symbfit{#2}
        \IfValueT{#4}%
          {\sp{#4\vphantom{\smash[t]{\big|}}}}
      }%
    \IfValueT{#3}%
      {\sb{#3\vphantom{\smash[b]{|}}}}
  }%
%    \end{macrocode}
%% A command for the direction of a vector.
% We use a slight tweak to get uniform hats that 
% requires the \href{https://www.ctan.org/pkg/makebox}{\pkg{makebox}} 
% package.\newline
% See \url{https://tex.stackexchange.com/a/391204/218142}.
%
%    \begin{macrocode}
\NewDocumentCommand{\dirvec}{ s m e{_^} }%
  {%
    \widehat%
      {%
        \makebox*{\(w\)}%
          {%
            \ensuremath{%
              \IfBooleanTF {#1}%
                {%
                  #2%
                }%
                {%
                  \symbfit{#2}%
                }%
            }%
          }%
      }%
    \IfValueT{#3}%
      {\sb{#3\vphantom{\smash[b]{|}}}}%
    \IfValueT{#4}%
      {\sp{\,#4\vphantom{\smash[t]{\big|}}}}%
  }%
%    \end{macrocode}
%
% The zero vector.
%
%    \begin{macrocode}
\NewDocumentCommand{\zerovec}{ s }%
  {%
    \IfBooleanTF {#1}
      {\vv{0}}%
      {\symbfup{0}}%
  }%
%    \end{macrocode}
%
% Notation for column and row vectors.\newline
% See \url{https://tex.stackexchange.com/a/39054/218142}.
%
%    \begin{macrocode}
\ExplSyntaxOn
\NewDocumentCommand{\colvec}{ O{,} m }
  {
    \__mandi_vectormain:nnnn { p } { \\ } { #1 } { #2 }
  }
\NewDocumentCommand{\rowvec}{ O{,} m }
  {
    \__mandi_vectormain:nnnn { p } { & } { #1 } { #2 }
  }
\seq_new:N \l__mandi_vectorarg_seq
\cs_new_protected:Npn \__mandi_vectormain:nnnn #1#2#3#4 
  {
    \seq_set_split:Nnn \l__mandi_vectorarg_seq { #3 } { #4 }
    \begin{#1NiceMatrix}[r]
      \seq_use:Nnnn \l__mandi_vectorarg_seq { #2 } { #2 } { #2 }
    \end{#1NiceMatrix}
  }
\ExplSyntaxOff
%    \end{macrocode}
%
% Students always need this symbol.
%
%    \begin{macrocode}
\NewDocumentCommand{\changein}{}{\Delta}
%    \end{macrocode}
%
% Intelligent delimiters provided via the 
% \href{https://www.ctan.org/pkg/mathtools}{\pkg{mathtools}} package.
% Use the starred variants for fractions. You can supply optional sizes.
% Note that default placeholders are used when the argument is empty.
%
%    \begin{macrocode}
\DeclarePairedDelimiterX{\doublebars}[1]{\lVert}{\rVert}{\ifblank{#1}{\:\cdot\:}{#1}}
\DeclarePairedDelimiterX{\singlebars}[1]{\lvert}{\rvert}{\ifblank{#1}{\:\cdot\:}{#1}}
\DeclarePairedDelimiterX{\anglebrackets}[1]{\langle}{\rangle}{\ifblank{#1}{\:\cdot\:}{#1}}
\DeclarePairedDelimiterX{\parentheses}[1]{(}{)}{\ifblank{#1}{\:\cdot\:}{#1}}
\DeclarePairedDelimiterX{\squarebrackets}[1]{\lbrack}{\rbrack}{\ifblank{#1}{\:\cdot\:}{#1}}
\DeclarePairedDelimiterX{\curlybraces}[1]{\lbrace}{\rbrace}{\ifblank{#1}{\:\cdot\:}{#1}}
%    \end{macrocode}
%
% Some semantic aliases. Because of the way \refCom{vec} and
% \refCom{dirvec} are defined, I reluctantly decided not to
% implement a |\magvec| command. It would require accounting
% for too many options. So \refCom{magnitude} is the new
% solution.
%
%    \begin{macrocode}
\NewDocumentCommand{\magnitude}{}{\doublebars}
\NewDocumentCommand{\norm}{}{\doublebars}
\NewDocumentCommand{\absolutevalue}{}{\singlebars}
%    \end{macrocode}
%
% Commands for two important geometric relationships. These are meant
% mainly to be subscripts.
%
%    \begin{macrocode}
\NewDocumentCommand{\parallelto}{}%
  {%
    \mkern3mu\vphantom{\perp}\vrule depth 0pt\mkern2mu\vrule depth 0pt\mkern3mu%
  }%
\NewDocumentCommand{\perpendicularto}{}{\perp}
%    \end{macrocode}
%
% An environment for problem statements. The starred variant gives 
% in-line lists. These are not defined if \textsc{beamer} is loaded.
%
% \changes{v3.2.0}{2023-08-01}
%   {\linktoplace{sec:mandib}{mandistudent} \refEnv{physicsproblem} and
%   \refEnv{physicsproblem*} are not defined if \textsc{beamer} is loaded}
%    \begin{macrocode}
\IfClassLoadedTF{beamer}
  {}%
  {%
    \NewDocumentEnvironment{physicsproblem}{ m }%
      {%
        \newpage%
        \section*{#1}%
        \newlist{parts}{enumerate}{2}%
        \setlist[parts]{label=\bfseries(\alph*)}%
      }%
      {}%
    \NewDocumentEnvironment{physicsproblem*}{ m }%
      {%
        \newpage%
        \section*{#1}%
        \newlist{parts}{enumerate*}{2}%
        \setlist[parts]{label=\bfseries(\alph*)}%
      }%
      {}%
    \NewDocumentCommand{\problempart}{}{\item}%
  }%
%    \end{macrocode}
%
% An environment for problem solutions. Equation numbering
% is consecutive through the document.
%
%    \begin{macrocode}
\NewDocumentEnvironment{physicssolution}{ +b }%
  {%
    \begin{align}
      #1
    \end{align}
  }%
  {}%
\NewDocumentEnvironment{physicssolution*}{ +b }%
  {%
    \begin{align*}
      #1
    \end{align*}
  }%
  {}%
%    \end{macrocode}
%
%  See \url{https://tex.stackexchange.com/q/570223/218142}.
%
%    \begin{macrocode}
\NewDocumentCommand{\reason}{ O{4cm} m }%
  {%
    &&\begin{minipage}{#1}\raggedright\small #2\end{minipage}%
  }%
%    \end{macrocode}
%
% Command for highlighting parts of, or entire, mathematical expressions.\newline
% This command is not defined if \textsc{beamer} is loaded.\newline
% See \url{https://texample.net/tikz/examples/beamer-arrows/}.\newline
% See also \url{https://tex.stackexchange.com/a/406084/218142}.\newline
% See also \url{https://tex.stackexchange.com/a/570858/218142}.\newline
% See also \url{https://tex.stackexchange.com/a/570789/218142}.\newline
% See also \url{https://tex.stackexchange.com/a/79659/218142}.\newline
% See also \url{https://tex.stackexchange.com/q/375032/218142}.\newline
% See also \url{https://tex.stackexchange.com/a/571744/218142}%
%
% \changes{v3.2.0}{2023-08-01}
%   {\linktoplace{sec:mandib}{mandistudent} \refCom{hilite} is
%   not defined if \textsc{beamer} is loaded}
%    \begin{macrocode}
\newcounter{tikzhighlightnode}
\NewDocumentCommand{\hilite}{ O{magenta!60} m O{rectangle} }%
  {%
    \stepcounter{tikzhighlightnode}%
    \tikzmarknode{highlighted-node-\number\value{tikzhighlightnode}}{#2}%
    \edef\temp{%
      \noexpand\AddToShipoutPictureBG{%
        \noexpand\begin{tikzpicture}[overlay,remember picture]%
        \noexpand\iftikzmarkoncurrentpage{highlighted-node-\number\value{tikzhighlightnode}}%
         \noexpand\node[inner sep=1.0pt,fill=#1,#3,fit=(highlighted-node-\number\value{tikzhighlightnode})]{};%
        \noexpand\fi
        \noexpand\end{tikzpicture}%
      }%
    }%
    \temp%
  }%
%    \end{macrocode}
%
% A simplified command for importing images.\newline
% See \url{https://tex.stackexchange.com/a/614478/218142}.
%
% \changes{v3.1.0}{2022-01-27}
%   {\linktoplace{sec:mandib}{mandistudent} Slightly modified \refCom{image}}
%    \begin{macrocode}
\NewDocumentCommand{\image}{ O{scale=1} m m m }%
  {%
    \par
    \begin{figure}[ht!]
      \centering%
      \includegraphics[#1]{#2}%
      \caption{#3}%
      \label{#4}%
    \end{figure}%
    \par
  }%
%    \end{macrocode}
%
% Intelligent commands for typesetting vector and tensor symbols and 
% components suitable for use with both coordinate-free and index 
% notations. Use starred form for index notation, unstarred form for 
% coordinate-free.
%
% Consider renaming these to |\vectorsym| and |\tensorsym|.
%
%    \begin{macrocode}
\NewDocumentCommand{\veccomp}{ s m }%
  {%
    \IfBooleanTF{#1}
      {%
        \symnormal{#2}%
      }%
      {%
        \symbfit{#2}%
      }%
  }%
\NewDocumentCommand{\tencomp}{ s m }%
  {%
    \IfBooleanTF{#1}%
      {%
        \symsfit{#2}%
      }%
      {%
        \symbfsfit{#2}%
      }%
  }%
%    \end{macrocode}
%
% Command to typeset tensor valence.
%
%    \begin{macrocode}
\NewDocumentCommand{\valence}{ s m m }%
  {%
    \IfBooleanTF{#1}%
      {%
        (#2,#3)%
      }%
      {%
        \binom{#2}{#3}%
      }%
  }%
%    \end{macrocode}
%
% Intelligent notation for contraction on pairs of slots.
%
%    \begin{macrocode}
\NewDocumentCommand{\contraction}{ s m }%
  {%
    \IfBooleanTF{#1}
      {%
        \mathsf{C}%
      }%
      {%
        \symbb{C}%
      }%
    _{#2}
  }%
%    \end{macrocode}
%
% Intelligent slot command for coordinate-free tensor notation.
% |d[]| must be used because of the way consecutive optional
% arguments are handled. See \pkg{xparse} docs for details.
%
%    \begin{macrocode}
\NewDocumentCommand{\slot}{ s d[] }%
  {%
    \IfBooleanTF{#1}
    {%
      \IfValueTF{#2}
%    \end{macrocode}
%
% Insert a vector, but don't show the slot.
%
%    \begin{macrocode}
      {%
        \smash{\makebox[1.5em]{\ensuremath{#2}}}
      }%
%    \end{macrocode}
%
% No vector, no slot.
%
%    \begin{macrocode}
      {%
        \smash{\makebox[1.5em]{\ensuremath{}}}
      }%
    }%
    {%
      \IfValueTF{#2}
%    \end{macrocode}
%
% Insert a vector and show the slot.
%
%    \begin{macrocode}
        {%
          \underline{\smash{\makebox[1.5em]{\ensuremath{#2}}}}
        }%
%    \end{macrocode}
%
% No vector; just show the slot.
%
%    \begin{macrocode}
        {%
          \underline{\smash{\makebox[1.5em]{\ensuremath{}}}}
        }%
    }%
  }%
%    \end{macrocode}
%
% Intelligent differential (exterior derivative) operator.
%
%    \begin{macrocode}
\NewDocumentCommand{\df}{ s }%
  {%
    \mathop{}\!%
    \IfBooleanTF{#1}%
      {%
        \symbfsfup{d}%
      }%
      {%
        \symsfup{d}%
      }%
  }%
%    \end{macrocode}
%
% Here is a clever way to color digits in program listsings thanks to
% Ulrike Fischer.\newline
% See \url{https://tex.stackexchange.com/a/570717/218142}.
%
%    \begin{macrocode}
\directlua{%
 luaotfload.add_colorscheme("colordigits",
   {["8000FF"] = {"one","two","three","four","five","six","seven","eight","nine","zero"}})
}%
\newfontfamily\colordigits{DejaVuSansMono}[RawFeature={color=colordigits}]
%    \end{macrocode}
%
% Set up a color scheme and a new code environment for listings. The new colors 
% are more restful on the eye. All listing commands now use 
% \href{https://www.ctan.org/pkg/tcolorbox}{\pkg{tcolorbox}}.\newline
% See \url{https://tex.stackexchange.com/a/529421/218142}.
%
% We set a new font for listings and some new colors (background gray, gray,
% green, orange, peach, pearl, and plum).
%
%    \begin{macrocode}
\newfontfamily{\gsfontfamily}{DejaVuSansMono}
\definecolor{gsbggray}     {rgb}{0.90,0.90,0.90}
\definecolor{gsgray}       {rgb}{0.30,0.30,0.30}
\definecolor{gsgreen}      {rgb}{0.00,0.60,0.00}
\definecolor{gsorange}     {rgb}{0.80,0.45,0.12}
\definecolor{gspeach}      {rgb}{1.00,0.90,0.71}
\definecolor{gspearl}      {rgb}{0.94,0.92,0.84}
\definecolor{gsplum}       {rgb}{0.74,0.46,0.70}
\lstdefinestyle{vpython}%
  {%
    backgroundcolor=\color{gsbggray},%           % background color
    basicstyle=\colordigits\footnotesize,%       % default style
    breakatwhitespace=true%                      % break at whitespace
    breaklines=true,%                            % break long lines
    captionpos=b,%                               % position caption
    classoffset=1,%                              % STILL DON'T UNDERSTAND THIS
    commentstyle=\color{gsgray},%                % font for comments
    deletekeywords={print},%                     % delete keywords from the given language
    emph={self,cls,@classmethod,@property},%     % words to emphasize
    emphstyle=\color{gsorange}\itshape,%         % font for emphasis
    escapeinside={(*@}{@*)},%                    % add LaTeX within your code
    frame=tb,%                                   % frame style
    framerule=2.0pt,%                            % frame thickness
    framexleftmargin=5pt,%                       % extra frame left margin
    %identifierstyle=\sffamily,%                 % style for identifiers
    keywordstyle=\gsfontfamily\color{gsplum},%   % color for keywords
    language=Python,%                            % select language
    linewidth=\linewidth,%                       % width of listings
    morekeywords={%                              % VPython/Web VPython specific keywords
      __future__,abs,acos,align,ambient,angle,append,append_to_caption,%
      append_to_title,arange,arrow,asin,astuple,atan,atan2,attach_arrow,%
      attach_trail,autoscale,axis,background,billboard,bind,black,blue,border,%
      bounding_box,box,bumpaxis,bumpmap,bumpmaps,camera,canvas,caption,capture,%
      ceil,center,clear,clear_trail,click,clone,CoffeeScript,coils,color,combin,%
      comp,compound,cone,convex,cos,cross,curve,cyan,cylinder,data,degrees,del,%
      delete,depth,descender,diff_angle,digits,division,dot,draw_complete,%
      ellipsoid,emissive,end_face_color,equals,explog,extrusion,faces,factorial,%
      False,floor,follow,font,format,forward,fov,frame,gcurve,gdisplay,gdots,%
      get_library,get_selected,ghbars,global,GlowScript,graph,graphs,green,gvbars,%
      hat,headlength,headwidth,height,helix,hsv_to_rgb,index,interval,keydown,%
      keyup,label,length,lights,line,linecolor,linewidth,logx,logy,lower_left,%
      lower_right,mag,mag2,magenta,make_trail,marker_color,markers,material,%
      max,min,mouse,mousedown,mousemove,mouseup,newball,norm,normal,objects,%
      offset,one,opacity,orange,origin,path,pause,pi,pixel_to_world,pixels,plot,%
      points,pos,pow,pps,print,print_function,print_options,proj,purple,pyramid,%
      quad,radians,radius,random,rate,ray,read_local_file,readonly,red,redraw,%
      retain,rgb_to_hsv,ring,rotate,round,scene,scroll,shaftwidth,shape,shapes,%
      shininess,show_end_face,show_start_face,sign,sin,size,size_units,sleep,%
      smooth,space,sphere,sqrt,start,start_face_color,stop,tan,text,textpos,%
      texture,textures,thickness,title,trail_color,trail_object,trail_radius,%
      trail_type,triangle,trigger,True,twist,unbind,up,upper_left,upper_right,%
      userpan,userspin,userzoom,vec,vector,vertex,vertical_spacing,visible,%
      visual,vpython,VPython,waitfor,Web,VPython,white,width,world,xtitle,%
      yellow,yoffset,ytitle%
    },%
    morekeywords={print,None,TypeError},%        % additional keywords
    morestring=[b]{"""},%                        % treat triple quotes as strings
    numbers=left,%                               % where to put line numbers
    numbersep=10pt,%                             % how far line numbers are from code
    numberstyle=\bfseries\tiny,%                 % set to 'none' for no line numbers
    showstringspaces=false,%                     % show spaces in strings
    showtabs=false,%                             % show tabs within strings
    stringstyle=\gsfontfamily\color{gsgreen},%   % color for strings
    upquote=true,%                               % how to typeset quotes
  }%
%    \end{macrocode}
%
% Introduce a new, more intelligent \refEnv{webvpythonblock} environment.\newline
% See \url{https://tex.stackexchange.com/a/232208/218142}.
%
% \changes{v3.1.0}{2022-01-27}
%   {\linktoplace{sec:mandib}{mandistudent} URLs fixed in \refEnv{webvpythonblock}}
% \changes{v3.1.0}{2022-01-27}
%   {\linktoplace{sec:mandib}{mandistudent} \refEnv{webvpythonblock} now automatically
%    generates QR codes for program listings}
% \changes{v3.2.0}{2023-08-01}
%   {\linktoplace{sec:mandib}{mandistudent} \refEnv{webvpythonblock} and 
%    \refEnv{webvpythonblock*} now show URL in header}
%    \begin{macrocode}
\AtBeginEnvironment{webvpythonblock}{\catcode`\#=12}
\AtEndEnvironment{webvpythonblock}{\catcode`\#=6}
\NewTCBListing[auto counter,list inside=gsprogs]{webvpythonblock}{ O{} D(){webvpython.org} m }%
  {%
    breakable,%
    center,%
    code = \newpage,%
    %derivpeach,%
    enhanced,%
    hyperurl interior = https://#2,%
    label = {gs:\thetcbcounter},%
    left = 8mm,%
    list entry = \thetcbcounter~~~~~#3,%
    listing only,%
    listing style = vpython,%
    nameref = {#3},%
    title = \begin{minipage}{1.5cm}%
              \protect\qrcode*{https://#2}%
            \end{minipage}\hspace{5mm}%
            \begin{minipage}{0.8\textwidth}%
              \texttt{Web VPython} Program \thetcbcounter: #3\\
              \footnotesize{\href{https://#2}{\color{white}{https://#2}}}%
            \end{minipage},%
    width = 0.9\textwidth,%
    {#1},
  }%
%    \end{macrocode}
%
% Here is a variant that omits the QR code.
%
% \changes{v3.1.0}{2022-01-27}
%   {\linktoplace{sec:mandib}{mandistudent} \refEnv{webvpythonblock*} is a variant
%    of \refEnv{webvpythonblock} that omits the QR code}
%    \begin{macrocode}
\AtBeginEnvironment{webvpythonblock*}{\catcode`\#=12}
\AtEndEnvironment{webvpythonblock*}{\catcode`\#=6}
\NewTCBListing[use counter from=webvpythonblock,list inside=gsprogs]
  {webvpythonblock*}{ O{} D(){webvpython.org} m }%
    {%
      breakable,%
      center,%
      code = \newpage,%
      %derivpeach,%
      enhanced,%
      hyperurl interior = https://#2,%
      label = {gs:\thetcbcounter},%
      left = 8mm,%
      list entry = \thetcbcounter~~~~~#3,%
      listing only,%
      listing style = vpython,%
      nameref = {#3},%
      title =   \texttt{Web VPython} Program \thetcbcounter: #3\\%
                \footnotesize{\href{https://#2}{\color{white}{https://#2}}},%
      width = 0.9\textwidth,%
      {#1},
    }%
%    \end{macrocode}
%
% A new command for generating a list of \WebVPython{} programs.
%
%    \begin{macrocode}
\NewDocumentCommand{\listofwebvpythonprograms}{}%
  {%
    \tcblistof[\section*]{gsprogs}{List of \texttt{Web VPython} Programs}%
  }%
%    \end{macrocode}
%
% Introduce a new, more intelligent \refCom{vpythonfile} command.\newline
% See \url{https://tex.stackexchange.com/q/616205/218142}.
% 
% \changes{v3.1.0}{2022-01-27}
%   {\linktoplace{sec:mandib}{mandistudent} URLs fixed in \refCom{vpythonfile}}
% \changes{v3.1.0}{2022-01-27}
%   {\linktoplace{sec:mandib}{mandistudent} Default URL for \refCom{vpythonfile}
%    is now \vpurl}
% \changes{v3.2.0}{2023-08-01}
%   {\linktoplace{sec:mandib}{mandistudent} \refCom{vpythonfile} now shows
%    URL in header}
%    \begin{macrocode}
\newcommand*{\vpythonfile}{\catcode`\#=12 \vpythonfile@auxA}
\NewDocumentCommand{\vpythonfile@auxA}{ O{} D(){vpython.org} m m }%
  {%
    \vpythonfile@auxB[#1](#2){#3}{#4}%
    \catcode`\#=6
  }%
\NewTCBInputListing[auto counter,list inside=vpprogs]
  {\vpythonfile@auxB}{ O{} D(){vpython.org} m m }%
    {%
      breakable,%
      center,%
      code = \newpage,%
      %derivgray,%
      enhanced,%
      hyperurl interior = https://#2,%
      label = {vp:\thetcbcounter},%
      left = 8mm,%
      list entry = \thetcbcounter~~~~~#4,%
      listing file = {#3},%
      listing only,%
      listing style = vpython,%
      nameref = {#4},%
      title = \texttt{VPython} Program \thetcbcounter: #4,%
      width = 0.9\textwidth,%
      {#1},%
    }%
%    \end{macrocode}
%
%  A new command for generating a list of \VPython{} programs.
%
%    \begin{macrocode}
\NewDocumentCommand{\listofvpythonprograms}{}%
  {%
    \tcblistof[\section*]{vpprogs}{List of \texttt{VPython} Programs}%
  }%
%    \end{macrocode}
%
% Introduce a new \refCom{webvpythoninline} command.
%
%    \begin{macrocode}
\DeclareTotalTCBox{\webvpythoninline}{ m }%
  {%
    bottom = 0pt,%
    bottomrule = 0.0mm,%
    boxsep = 1.0mm,%
    colback = gsbggray,%
    colframe = gsbggray,%
    left = 0pt,%
    leftrule = 0.0mm,%
    nobeforeafter,%
    right = 0pt,%
    rightrule = 0.0mm,%
    sharp corners,%
    tcbox raise base,%
    top = 0pt,%
    toprule = 0.0mm,%
  }%
  {\lstinline[style = vpython]{#1}}%
%    \end{macrocode}
%
% Define \refCom{vpythoninline}, a semantic alias for \VPython{} 
% in-line listings.
%
%    \begin{macrocode}
\NewDocumentCommand{\vpythoninline}{}{\webvpythoninline}%
%    \end{macrocode}
%
% \restoregeometry
%
% \iffalse
%</package>
% \fi
%
% \Finale
