-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy path1_introduction.html
More file actions
378 lines (365 loc) · 26.3 KB
/
1_introduction.html
File metadata and controls
378 lines (365 loc) · 26.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
<!DOCTYPE html>
<html lang="en" data-content_root="./">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<title>1. Introduction: abstraction in mathematics and programming — Object-oriented Programming documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=03e43079" />
<link rel="stylesheet" type="text/css" href="_static/fenics.css?v=8c7d05f9" />
<link rel="stylesheet" type="text/css" href="_static/proof.css" />
<link rel="stylesheet" type="text/css" href="_static/graphviz.css?v=fd3f3429" />
<script src="_static/documentation_options.js?v=5929fcd5"></script>
<script src="_static/doctools.js?v=9a2dae69"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/proof.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
<link rel="next" title="2. Programs in files" href="2_programs_in_files.html" />
<link rel="prev" title="Preface" href="0_preface.html" />
<!--[if lte IE 6]>
<link rel="stylesheet" href="_static/ie6.css" type="text/css" media="screen" charset="utf-8" />
<![endif]-->
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-0EFVH5C4DC"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-0EFVH5C4DC');
</script>
<link rel="stylesheet" href="_static/featured.css">
<link rel="shortcut icon" href="_static/icon.ico" />
</head><body>
<div class="wrapper">
<a href="index.html"><img src="_static/banner.png" width="900px" alt="FInAT Project Banner" /></a>
<div id="access">
<div class="menu">
<ul>
<li class="page_item"><a href="index.html" title="Book">Book</a></li>
<li class="page_item"><a href="videos.html" title="Videos">Videos</a></li>
<li class="page_item"><a href="exercises.html"
title="Exercises">Exercises</a></li>
<li class="page_item"><a href="installation.html" title="Installation">Installation</a></li>
</ul>
</div><!-- .menu -->
</div><!-- #access -->
</div><!-- #wrapper -->
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section id="introduction-abstraction-in-mathematics-and-programming">
<span id="introduction"></span><h1><span class="section-number">1. </span>Introduction: abstraction in mathematics and programming<a class="headerlink" href="#introduction-abstraction-in-mathematics-and-programming" title="Link to this heading">¶</a></h1>
<p>A core tool of mathematics is to define abstract objects and the
operations which apply to them. This approach defines all the basic
building blocks which enable us to reason mathematically and perform
calculations. We start off with basic objects like numbers and define
arithmetic operations on them. As we become more sophisticated, we
define more and more complex objects, with appropriately more involved
operations: matrices, polynomials, sets, groups, algebras. Being able
to reason at the level of abstract objects is essential in making
mathematics comprehensible. Consider matrices: linear algebra would be
highly impractical at best if we could not define matrix addition and
multiplication, and had instead to work directly with sums of products
of scalars. More generally, without abstraction the edifice of higher
mathematics would rapidly collapse under the weight of its own
complexity.</p>
<p>The situation in computer programming is strikingly similar. At the
simplest level, the central processing unit of a computer is capable
only of a limited set of rather primitive arithmetic and logical
operations on a few finite subsets of the integers, and of the
floating point numbers. However, on this tiny foundation is built
every piece of software in existence, including very sophisticated
programs for text manipulation, higher mathematics, sound and
video. Moreover, this software is routinely created by ordinary people
using relatively modest amounts of effort. How is this possible? It is
possible because of the same principle of abstraction which underpins
mathematics: more abstract objects and the operations on them are
defined in terms of simpler ones.</p>
<p>Think about plotting a graph on a computer. As a 21st century
mathematician you don’t write loops over arrays to compute the pixel
values that will result in the right curve appearing on a screen, you
use a high-level language such as Python which has plotting objects
which take in data and perform all of those calculations for
you. Where did those plotting objects come from? They are the result
of the manipulation of lower-level abstract objects in a chain that
eventually ends up with primitive operations on integers and floating
point numbers. But, critically, as the person wanting to plot some
data <em>you don’t care</em>.</p>
<p>Why, then, as a mathematician should you care about abstraction in
programming? There are two key reasons. First, because an
understanding of the abstractions on which software is built will give
you a better understanding of how that software works and will
therefore make you a better user of that software. The second is that
making appropriate use of abstractions will make you a better
programmer of mathematics and other software. Applied well, objects
and abstraction produce software which is easier to write, easier to
understand, easier to debug, and easier to extend. Indeed, as with
abstraction in mathematics, abstraction in coding is a form of
constructive laziness: it simultaneously allows the mathematician to
achieve more and do less work.</p>
<section id="obtaining-the-right-software-tools">
<span id="tools"></span><h2><span class="section-number">1.1. </span>Obtaining the right software tools<a class="headerlink" href="#obtaining-the-right-software-tools" title="Link to this heading">¶</a></h2>
<p>In order to do the exercises in this book, you’ll need some core software
tools. At various points you’ll also need install several more Python packages,
but you don’t need to install those right now. The core tools you will need
are:</p>
<blockquote>
<div><ol class="arabic simple">
<li><p>Python version 3.10 or later (3.13 or later recommended).</p></li>
<li><p>Git (the revision control system we’re going to use).</p></li>
<li><p>A Python-aware text editor or <a class="reference internal" href="#term-integrated-development-environment"><span class="xref std std-term">integrated development
environment</span></a> (IDE). Visual Studio Code is recommended, and all the
instructions will assume that this is what you are using.</p></li>
</ol>
</div></blockquote>
<p>Installation instructions for these pieces of code are provided <a class="reference internal" href="installation.html"><span class="doc">on
this page</span></a>.</p>
<p>This is not a course about Git, but a minimal familiarity with Git and GitHub
will be needed in order to work with the examples. <a class="reference internal" href="a2_git.html#git"><span class="std std-numref">Appendix 2</span></a>
provides an elementary introduction to these tools for readers not already
familiar with them.</p>
<section id="python-versions">
<h3><span class="section-number">1.1.1. </span>Python versions<a class="headerlink" href="#python-versions" title="Link to this heading">¶</a></h3>
<p>Python is a living language which regularly releases new versions. One very big
transition, which took over a decade, was the switch from version 2 to version
3 of the language. For many years both versions were in widespread use and
texts needed to cover their differences. However, Python 2 officially reached
end of life on 1 January 2020 and not even critical security updates are now
made to that version. You should, therefore, only ever use Python 3, and all
references in this book are to that version.</p>
<p>Within Python 3, there is a minor version release approximately every year.
Once released, this receives security updates for 5 years. At the time of
writing, Python 3.14 is about to be released, and Python 3.10 is the
oldest version that still receives security fixes. The user-facing differences
between minor Python versions are usually fairly minimal, so for the purposes
of this book it doesn’t matter which of the currently supported versions of
Python you use, though it is usually advantageous to use the newest version you
can conveniently install.</p>
</section>
<section id="running-the-right-python-version">
<span id="run-version"></span><h3><span class="section-number">1.1.2. </span>Running the right Python version<a class="headerlink" href="#running-the-right-python-version" title="Link to this heading">¶</a></h3>
<p>Once we have created a virtual environment, we will be able to run Python just
by typing <code class="xref py py-obj docutils literal notranslate"><span class="pre">python</span></code>. However, before we get there, what we need to do in order
to run Python differs a little depending on the operating system, how we
installed Python, and whether there are multiple versions of Python installed
on the computer.</p>
<p>On Windows, the <code class="xref py py-obj docutils literal notranslate"><span class="pre">py</span></code> command acts as a general launcher for Python. The
following command will print out the version of Python that it will launch by
default:</p>
<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="go">> py --version</span>
</pre></div>
</div>
<p>On the author’s computer, this prints <code class="xref py py-obj docutils literal notranslate"><span class="pre">Python</span> <span class="pre">3.13.3</span></code>, which is the version of
Python I expect to be using. This means I can launch Python on Windows using
just <code class="xref py py-obj docutils literal notranslate"><span class="pre">py</span></code>. If it printed a different version, then I could attempt to force it
to use the version I want like this:</p>
<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="go">> py -3.13 --version</span>
</pre></div>
</div>
<p>If that fails, then there is an issue with your Python documentation and you
need to go back to the start of this section to work out how to install the
right version.</p>
<p>On Mac or Linux, the safest way to ensure that you are running the right
version of Python is to use the full version number, e.g. <code class="xref py py-obj docutils literal notranslate"><span class="pre">python3.13</span></code>. You can
check this with, for example:</p>
<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp">$ </span>python3.13<span class="w"> </span>--version
</pre></div>
</div>
<p>If this fails, then the relevant Python version isn’t (correctly) installed and
you will need to use a different version, or install it.</p>
</section>
</section>
<section id="setting-up-a-python-virtual-environment">
<span id="create-venv"></span><h2><span class="section-number">1.2. </span>Setting up a Python virtual environment<a class="headerlink" href="#setting-up-a-python-virtual-environment" title="Link to this heading">¶</a></h2>
<details>
<summary>
Video: setting up your virtual environment.</summary><div class="video_wrapper" style="">
<iframe allowfullscreen="true" src="https://player.vimeo.com/video/486546635" style="border: 0; height: 345px; width: 560px">
</iframe></div><p>Imperial students can also <a class="reference external" href="https://imperial.cloud.panopto.eu/Panopto/Pages/Viewer.aspx?id=d63e1c6e-18ff-48de-af03-ae1c00da3b3f">watch this video on Panopto</a></p>
</details><p>In the course of the exercises, You’re going to create, edit, and install a
whole bunch of Python packages. It’s highly desirable have a predictable
programming environment in which the experiments you’re doing don’t interfere
with anything else for which you might be using Python, and conversely which
remains unaffected by any packages you may have installed elsewhere. This
separation can be achieved by working in a Python <a class="reference internal" href="#term-virtual-environment"><span class="xref std std-term">virtual
environment</span></a>, or <a class="reference internal" href="#term-venv"><span class="xref std std-term">venv</span></a>.</p>
<p>A virtual environment is a folder containing a local installation of Python
which links back to the Python you installed on your computer. This means that
virtual environments behave like separate Python installations for most
purposes, but are fast to install and take very little space because they share
most of their files with the already installed Python.</p>
<section id="creating-a-working-folder">
<span id="working-folder"></span><h3><span class="section-number">1.2.1. </span>Creating a working folder<a class="headerlink" href="#creating-a-working-folder" title="Link to this heading">¶</a></h3>
<p>Start by creating a completely fresh folder for your work on this book. You can
call this anything you like. On my computer this is called
<code class="file docutils literal notranslate"><span class="pre">principles_of_programming</span></code>. You can create this folder using the
Windows File Explorer, Mac Finder, or by typing the following in a terminal:</p>
<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp">$ </span>mkdir<span class="w"> </span>principles_of_programming
</pre></div>
</div>
<p>Obviously you replace <code class="file docutils literal notranslate"><span class="pre">principles_of_programming</span></code> with whatever you
decide to call the folder. The dollar sign is the command prompt. Its different
on some systems, for example, it’s often a greater than sign (<code class="xref py py-obj docutils literal notranslate"><span class="pre">></span></code>) or a percent
symbol (<code class="xref py py-obj docutils literal notranslate"><span class="pre">%</span></code>). The text to the left of the command prompt might also be
different depending on which terminal program you are using on which operating
system, but we are only concerned with the commands to the right of the prompt.</p>
<div class="admonition hint">
<p class="admonition-title">Hint</p>
<p>Modern operating systems are quite capable of dealing with folder names and
file names containing spaces. However, there are many pieces of software
(including some Python packages) that don’t correctly deal with spaces in
folder and file names. It’s therefore a safer option to avoid spaces and
instead to separate words with underscores (<code class="file docutils literal notranslate"><span class="pre">_</span></code>).</p>
</div>
</section>
<section id="creating-the-venv">
<h3><span class="section-number">1.2.2. </span>Creating the venv<a class="headerlink" href="#creating-the-venv" title="Link to this heading">¶</a></h3>
<p>The most straightforward way to create a venv is on the terminal
command line, not from within Python itself. This is accomplished
using Python’s <a class="reference external" href="https://docs.python.org/3/library/venv.html#module-venv" title="(in Python v3.14)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">venv</span></code></a> package. The venv has to be given a name. You will
want this to be short, but distinctive enough that you know which venv you are
using. For example, to create a venv
called <code class="xref py py-obj docutils literal notranslate"><span class="pre">PoP_venv</span></code> on Windows, you would type:</p>
<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="go">> py -3.13 -m venv PoP_venv</span>
</pre></div>
</div>
<p>while on Mac or Linux you would type:</p>
<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp">$ </span>python3.13<span class="w"> </span>-m<span class="w"> </span>venv<span class="w"> </span>PoP_venv
</pre></div>
</div>
<p>If you’re using a different version of Python then modify the command according
to the discussion in section <a class="reference internal" href="#run-version"><span class="std std-numref">Section 1.1.2</span></a>.
Don’t forget that the <code class="xref py py-obj docutils literal notranslate"><span class="pre">></span></code> or <code class="xref py py-obj docutils literal notranslate"><span class="pre">$</span></code> stands for the command prompt: you don’t
type it. This command will create the folder <code class="xref py py-obj docutils literal notranslate"><span class="pre">PoP_venv</span></code> and various
subfolders containing things like the Python program itself and space
for any packages which you install in the venv. If there was already a
file or folder called <code class="xref py py-obj docutils literal notranslate"><span class="pre">PoP_venv</span></code> in the current folder then you’ll get
an error, so make sure you choose a new name.</p>
<div class="admonition note">
<p class="admonition-title">Note</p>
<p>Once the venv is installed and activated, it will be
sufficient to type <code class="xref py py-obj docutils literal notranslate"><span class="pre">python</span></code> as the venv will ensure that this is the
correct version.</p>
</div>
<p>A venv doesn’t usually contain any particularly valuable data, so you
should regard them as essentially disposable. In particular, if
something goes wrong when creating a venv, just delete it and start
again. In the bash or zsh shells you would type:</p>
<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp">$ </span>rm<span class="w"> </span>-rf<span class="w"> </span>PoP_venv
</pre></div>
</div>
<div class="admonition warning">
<p class="admonition-title">Warning</p>
<p><code class="xref py py-obj docutils literal notranslate"><span class="pre">rm</span> <span class="pre">-rf</span></code> will delete its argument and all its subdirectories
without further prompts or warnings. There is no undo operation.
Be very careful about what you delete.</p>
</div>
</section>
<section id="using-a-venv">
<span id="activate-venv"></span><h3><span class="section-number">1.2.3. </span>Using a venv<a class="headerlink" href="#using-a-venv" title="Link to this heading">¶</a></h3>
<p>If you run Python from the terminal, then the simplest way to use the
venv is to source its activate script. If using bash or zsh on Mac or
Linux you would type:</p>
<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp">$ </span><span class="nb">source</span><span class="w"> </span>PoP_venv/bin/activate
</pre></div>
</div>
<p>while using bash on Windows you would type:</p>
<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp">$ </span><span class="nb">source</span><span class="w"> </span>PoP_venv/Scripts/activate
</pre></div>
</div>
<p>If using PowerShell on Windows then you type:</p>
<div class="highlight-powershell notranslate"><div class="highlight"><pre><span></span><span class="p">></span> <span class="p">.\</span><span class="n">PoP_venv</span><span class="p">\</span><span class="n">Scripts</span><span class="p">\</span><span class="n">activate</span><span class="p">.</span><span class="n">ps1</span>
</pre></div>
</div>
<p>Obviously, you would use the folder name of your venv instead of
<code class="xref py py-obj docutils literal notranslate"><span class="pre">PoP_venv</span></code>. In either case, your command prompt will change to indicate
that you are now using the venv. It might look something like:</p>
<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp gp-VirtualEnv">(PoP_venv)</span> <span class="gp">$</span>
</pre></div>
</div>
<p>Any subsequent invocations of Python commands such as <code class="xref py py-obj docutils literal notranslate"><span class="pre">python</span></code> will
now use the version from the venv, with access to whatever packages
you have installed in that venv. If you are using a terminal shell
other than bash or zsh, then see the <a class="reference external" href="https://docs.python.org/3/library/venv.html#module-venv" title="(in Python v3.14)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">venv</span></code></a> package documentation
for the correct activation command.</p>
<div class="admonition hint">
<p class="admonition-title">Hint</p>
<p>The default permissions settings on your Windows computer may not permit you
to run the activation script. This can be fixed by running:</p>
<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="go">> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser</span>
</pre></div>
</div>
<p>For further information, see <a class="reference external" href="https://docs.python.org/3/library/venv.html" title="(in Python v3.14)"><span class="xref std std-doc">the official Python venv
documentation</span></a>.</p>
</div>
<div class="admonition hint">
<p class="admonition-title">Hint</p>
<p>Venv activation is just for one terminal session. You need to activate the
venv every time you open a new terminal, though if you are lucky then
Visual Studio Code will notice the venv and activate it for you. If you
find that Python can’t find your packages or tests, then the first thing to
check is whether you remembered to activate the venv.</p>
</div>
</section>
</section>
<section id="installing-python-packages">
<h2><span class="section-number">1.3. </span>Installing Python packages<a class="headerlink" href="#installing-python-packages" title="Link to this heading">¶</a></h2>
<p>Suppose we’ve created and activated a venv, and now there’s a Python package
we’d like to have access to. Installation of Python packages is handled by the
Python package <a class="reference external" href="https://pip.pypa.io/en/stable/" title="(in pip v25.3)"><span class="xref std std-doc">Pip</span></a>, which you will usually find
pre-installed in your Python installation. Pip has many usage options, which
enable a large number of different installation configurations. However, for
most users most of the time, a few simple pip commands suffice. As with
venv creation, package installation is best accomplished from the
terminal and not from within Python itself. Don’t forget to activate the venv!</p>
<section id="installing-packages-from-pypi">
<span id="install-from-pypi"></span><h3><span class="section-number">1.3.1. </span>Installing packages from PyPI<a class="headerlink" href="#installing-packages-from-pypi" title="Link to this heading">¶</a></h3>
<p><a class="reference external" href="https://pypi.org">PyPI</a> is the Python Package Index. It is the
official download location for publicly released Python packages which
aren’t themselves a part of the built-in <a class="reference external" href="https://docs.python.org/3/library/index.html" title="(in Python v3.14)"><span class="xref std std-doc">Python Standard Library</span></a>. Many important mathematical packages
including <a class="reference external" href="https://numpy.org/doc/stable/reference/index.html#module-numpy" title="(in NumPy v2.3)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">numpy</span></code></a> and <a class="reference external" href="https://www.sympy.org">sympy</a> are
distributed from PyPI. Suppose your venv doesn’t have <a class="reference external" href="https://numpy.org/doc/stable/reference/index.html#module-numpy" title="(in NumPy v2.3)"><code class="xref py py-mod docutils literal notranslate"><span class="pre">numpy</span></code></a>
installed and you need it. You would install it with the following
terminal command:</p>
<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp gp-VirtualEnv">(PoP_venv)</span> <span class="gp">$ </span>python<span class="w"> </span>-m<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>numpy
</pre></div>
</div>
<p>It is also possible to invoke pip directly using the command <code class="xref py py-obj docutils literal notranslate"><span class="pre">pip3</span></code>,
but there are some circumstances where that might result in pip using
the wrong Python installation. The approach used here is safer.</p>
<p>Python packages may depend on other Python packages, so it’s quite
likely that pip will install more packages than those you directly
asked for. This is necessary if those packages are to actually work.</p>
<p>Pip can also be used to upgrade a package to the latest version:</p>
<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp gp-VirtualEnv">(PoP_venv)</span> <span class="gp">$ </span>python<span class="w"> </span>-m<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>--upgrade<span class="w"> </span>numpy
</pre></div>
</div>
</section>
</section>
<section id="glossary">
<h2><span class="section-number">1.4. </span>Glossary<a class="headerlink" href="#glossary" title="Link to this heading">¶</a></h2>
<dl class="simple glossary">
<dt id="term-IDE">IDE<a class="headerlink" href="#term-IDE" title="Link to this term">¶</a></dt><dt id="term-integrated-development-environment">integrated development environment<a class="headerlink" href="#term-integrated-development-environment" title="Link to this term">¶</a></dt><dd><p>A program designed to help a software developer write code. An IDE
combines a text editor with features such as syntax highlighting and
checking, debugging capabilities, revision control interfaces and
inbuilt terminal windows.</p>
</dd>
<dt id="term-venv">venv<a class="headerlink" href="#term-venv" title="Link to this term">¶</a></dt><dt id="term-virtual-environment">virtual environment<a class="headerlink" href="#term-virtual-environment" title="Link to this term">¶</a></dt><dd><p>A lightweight private Python installation with its own set of
Python packages installed.</p>
</dd>
</dl>
</section>
</section>
<div class="clearer"></div>
</div>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="footer" role="contentinfo">
© Copyright 2019-2023, David A. Ham.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 7.4.7.
</div>
</body>
</html>