{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:How-to-package-a-Python)=\n",
    "# How to package a Python\n",
    "<hr>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this chapter we will develop an entire example Python package from beginning-to-end to demonstrate the key steps involved in developing a package. This chapter forms the foundation of this book. It contains everything you need to know to create a Python package and can be used as a reference sheet when creating packages in the future. Later chapters explore each of the individual steps in the packaging process in further detail.\n",
    "\n",
    "The example package we are going to create in this chapter will help us calculate word counts from a text file. We'll be calling it `pycounts`, and it will be useful for calculating word usage in texts such as novels, research papers, news articles, log files, and more."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Counting words in a text file"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Developing-our-code)=\n",
    "### Developing our code"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before even thinking about making a package, we'll first develop the code we want to package up. The `pycounts` package we are going to create will help us calculate word counts from a text file. Python has a useful `Counter` object that can be used to calculate counts of a collection of elements (like a list of words) and store them in a dictionary.\n",
    "\n",
    "We can demonstrate the functionality of `Counter` by first opening up a Python interpreter by typing `python` at the command line:\n",
    "\n",
    "```\n",
    "$ python\n",
    "```\n",
    "\n",
    "We can then import the `Counter` class from the `collections` module:\n",
    "\n",
    "```\n",
    ">>> from collections import Counter\n",
    "```\n",
    "\n",
    "Now we will define and use a sample list of words to create a `Counter` object:\n",
    "\n",
    "```\n",
    ">>> words = [\"a\", \"happy\", \"hello\", \"a\", \"world\", \"happy\"]\n",
    ">>> word_counts = Counter(words)\n",
    ">>> word_counts\n",
    "```\n",
    "\n",
    "```md\n",
    "Counter({'a': 2, 'happy': 2, 'hello': 1, 'world': 1})\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note how the `Counter` object automatically calculated the count of each unique word in our input list and returned the result as a dictionary of `'word': count` pairs! Given this functionality, how can we use `Counter` to count the words in a text file? Well, we would need to load the file with Python, split it up into a list of words, and then create a `Counter` object from that list of words.\n",
    "\n",
    "We first need a text file to help us build this workflow. \"[The Zen of Python](https://www.python.org/dev/peps/pep-0020/)\" is a list of nineteen aphorisms about the Python programming language, which can be viewed by running `import this` in a Python interpreter:\n",
    "\n",
    "```\n",
    ">>> import this\n",
    "```\n",
    "\n",
    "```md\n",
    "The Zen of Python, by Tim Peters\n",
    "\n",
    "Beautiful is better than ugly.\n",
    "Explicit is better than implicit.\n",
    "Simple is better than complex.\n",
    "...\n",
    "```\n",
    "\n",
    "Let's make a text file called *`zen.txt`* containing the \"The Zen of Python\" text above. Do this by manually copying the above output into a file in your current directory called *`zen.txt`* using an editor of your choice, or by running the following command at the command line:\n",
    "\n",
    "```\n",
    "$ python -c \"import this\" > zen.txt\n",
    "```\n",
    "\n",
    "```{note}\n",
    "In the command above, the `-c` option allows you to pass a string for Python to execute, and the `>` directs the output of the command to a file (which in our case is called \"zen.txt\" and is located in the current directory).\n",
    "```\n",
    "\n",
    "Now that we have a text file to work with, we can go back to developing our word-counting workflow. To open *`zen.txt`* in Python, we can use the `open()` function to open the file and then the `.read()` method to read its contents as a Python string. The code below, run in a Python interpreter, saves the contents of *`zen.txt`* as a string in the variable `text`:\n",
    "\n",
    "```\n",
    ">>> with open(\"zen.txt\") as file:\n",
    "        text = file.read()\n",
    "```\n",
    "\n",
    "Let's see what `text` looks like:\n",
    "\n",
    "```\n",
    ">>> text\n",
    "```\n",
    "\n",
    "```md\n",
    "\"The Zen of Python, by Tim Peters\\n\\nBeautiful is better\n",
    "than ugly.\\nExplicit is better than implicit.\\nSimple is \n",
    "better than complex.\\nComplex is better than complicated\n",
    "...\"\n",
    "```\n",
    "\n",
    "We can see that the `text` variable is a single string, with the `\\n` symbols indicating a new line in the string.\n",
    "\n",
    "Before we split the above text into individual words for counting with `Counter`, we should lowercase all the letters and remove punctuation so that if the same word occurs multiple times with different capitalization or punctuation, it isn't treated as different words by `Counter`. For example we want \"Better\", \"better\", and \"better!\" to result in three counts of the word \"better\".\n",
    "\n",
    "To lowercase all letters in a Python string, we can use the `.lower()` method:\n",
    "\n",
    "```\n",
    ">>> text = text.lower()\n",
    "```\n",
    "\n",
    "To remove punctuation, we can find them in our string and replace them with nothing using the `.replace()` method. Python provides a collection of common punctuation marks in the `string` module:\n",
    "\n",
    "```\n",
    ">>> from string import punctuation\n",
    ">>> punctuation\n",
    "```\n",
    "\n",
    "```md\n",
    "'!\"#$%&\\'()*+,-./:;<=>?@[\\\\]^_`{|}~'\n",
    "```\n",
    "\n",
    "We can use a `for` loop to remove each of the above punctuation marks from our `text` variable by replacing it with nothing, i.e., an empty string (`\"\"`):\n",
    "\n",
    "```\n",
    ">>> for p in punctuation:\n",
    "        text = text.replace(p, \"\")\n",
    "```\n",
    "\n",
    "With punctuation removed and the letters in `text` all lowercase, we can now split it up into individual words using the `.split()` method. This method splits a string into a list of strings using spaces, newlines (`\\n`), and tabs (`\\t`) as separators:\n",
    "\n",
    "```\n",
    ">>> words = text.split()\n",
    ">>> words\n",
    "```\n",
    "\n",
    "```md\n",
    "['the', 'zen', 'of', 'python', 'by', 'tim', 'peters', \n",
    "'beautiful', 'is', 'better', 'than', 'ugly', ...]\n",
    "```\n",
    "\n",
    "We've managed to load, pre-process, and split our *`zen.txt`* file up into individual words and can now determine the word counts by creating a `Counter` object:\n",
    "\n",
    "```\n",
    ">>> from collections import Counter\n",
    ">>> word_counts = Counter(words)\n",
    ">>> word_counts\n",
    "```\n",
    "\n",
    "```md\n",
    "Counter({'is': 10, 'better': 8, 'than': 8, 'the': 6, \n",
    "'to': 5, 'of': 3, 'although': 3, 'never': 3, ... })\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Turning-our-code-into-functions)=\n",
    "### Turning our code into functions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In **{numref}`03:Developing-our-code`** we developed a workflow for counting words in a text file. But it would be a pain to run all that code every time we want to count the words in a file! To make things more efficient, let’s turn the above code into three reusable functions called `load_text()`, `clean_text()`, and `count_words()` by defining them in our Python interpreter:\n",
    "\n",
    "```{tip}\n",
    "We've added a short documentation string (docstring) to each function here using triple quotes. We'll talk more about docstrings in **{numref}`03:Writing-docstrings`**.\n",
    "```\n",
    "\n",
    "```\n",
    ">>> def load_text(input_file):\n",
    "        \"\"\"Load text from a text file and return as a string.\"\"\"\n",
    "        with open(input_file, \"r\") as file:\n",
    "            text = file.read()\n",
    "        return text\n",
    "```\n",
    "\n",
    "```\n",
    ">>> def clean_text(text):\n",
    "        \"\"\"Lowercase and remove punctuation from a string.\"\"\"\n",
    "        text = text.lower()\n",
    "        for p in punctuation:\n",
    "            text = text.replace(p, \"\")\n",
    "        return text\n",
    "```\n",
    "\n",
    "```\n",
    ">>> def count_words(input_file):\n",
    "        \"\"\"Count unique words in a string.\"\"\"\n",
    "        text = load_text(input_file)\n",
    "        text = clean_text(text)\n",
    "        words = text.split()\n",
    "        return Counter(words)\n",
    "```\n",
    "\n",
    "We can now use our word-counting functionality as follows:\n",
    "\n",
    "```\n",
    ">>> count_words(\"zen.txt\")\n",
    "```\n",
    "\n",
    "```md\n",
    "Counter({'is': 10, 'better': 8, 'than': 8, 'the': 6, \n",
    "'to': 5, 'of': 3, 'although': 3, 'never': 3, ... })\n",
    "```\n",
    "\n",
    "Unfortunately, if you quit from the Python interpreter, the functions we just defined will be lost and you will have to define them again in new sessions.\n",
    "\n",
    "The whole idea of a Python package is that we can store Python code, like our `load_text()`, `clean_text()`, and `count_words()` functions, in a package that we, and others, can install, `import`, and use at any time and in any project. In the remainder of this chapter, we'll work towards packaging up the code we've written into a Python package called `pycounts`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Package structure"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:A-brief-introduction)=\n",
    "### A brief introduction"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To develop our `pycounts` package we first need to create an appropriate directory structure. Python packages consist of a specific directory structure typically including the following:\n",
    "\n",
    "- A root directory with the name of the package, e.g., *`pycounts/`*;\n",
    "- One or more Python modules\\index{module} (files with a *.py* extension that contain Python code) in a subdirectroy *`src/pycounts/`*;\n",
    "- Instructions on how to build and install the package on a computer in a file called *`pyproject.toml`*\\index{pyproject.toml};\n",
    "- Important documentation such as a README in the root directory, and additional documentation in a *`docs/`* subdirectory; and,\n",
    "- Tests in a *`tests/`* subdirectory.\n",
    "\n",
    "An example structure for a package called \"pycounts\" with two modules (\"moduleA\" and \"moduleB\") is shown below. There's a lot of files here, but don't worry; packages are usually created from pre-made templates, as we'll show in the next section. At this point, we're just getting a bird's-eye view of package structure\\index{package structure}. We'll create and explore each element in this structure as we make our way through this chapter.\n",
    "\n",
    "```md\n",
    "pycounts\n",
    "├── CHANGELOG.md               ┐\n",
    "├── CONDUCT.md                 │\n",
    "├── CONTRIBUTING.md            │\n",
    "├── docs                       │ Package documentation\n",
    "│   └── ...                    │\n",
    "├── LICENSE                    │\n",
    "├── README.md                  ┘\n",
    "├── pyproject.toml             ┐ \n",
    "├── src                        │\n",
    "│   └── pycounts               │ Package source code, metadata,\n",
    "│       ├── __init__.py        │ and build instructions \n",
    "│       ├── moduleA.py         │\n",
    "│       └── moduleB.py         ┘\n",
    "└── tests                      ┐\n",
    "    └── ...                    ┘ Package tests\n",
    "```\n",
    "\n",
    "\\newpage\n",
    "\n",
    "```{tip}\n",
    "It might be confusing to see two directories with the package's name (the root directory `pycounts/` and the subdirectory *`src/pycounts/`*, but this is how Python packages are typically set up. We'll explore this structure more in the rest of this chapter and discuss it in detail in **Chapter 4: {ref}`04:Package-structure-and-distribution`**.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Creating-a-package-structure)=\n",
    "### Creating a package structure"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Most developers use a pre-made template to set up the directory structure of a Python package. We will use the `cookiecutter`\\index{cookiecutter} tool (which we installed in **{numref}`02:Install-packaging-software`**) to create our package structure\\index{package structure} for us.\n",
    "\n",
    "`cookiecutter` is a tool for populating a directory structure from a pre-made template. People have developed and open-sourced many `cookiecutter` templates for different projects, such as for creating Python packages, R packages, websites, and more. You can find these templates by, for example, searching an online hosting service like [GitHub](https://www.github.com). We have developed our own `py-pkgs-cookiecutter`\\index{py-pkgs-cookiecutter} Python package template to support this book; it is [hosted on GitHub](https://github.com/py-pkgs/py-pkgs-cookiecutter).\n",
    "\n",
    "To use this template to create a package directory structure, you can navigate to the directory where you want to create your package from the command line, and then run the command below. Upon executing this command you will be prompted to provide information that will be used to create your package file and directory structure. We provide an example of how to respond to these prompts below and an explanation of what they mean in {numref}`03-prompt-table`.\n",
    "\n",
    "```\n",
    "$ cookiecutter https://github.com/py-pkgs/py-pkgs-cookiecutter.git\n",
    "```\n",
    "\n",
    "\\newpage\n",
    "\n",
    "```md\n",
    "author_name [Monty Python]: Tomas Beuzen\n",
    "package_name [mypkg]: pycounts\n",
    "package_short_description []: Calculate word counts in a text file!\n",
    "package_version [0.1.0]: \n",
    "python_version [3.9]: \n",
    "Select open_source_license:\n",
    "1 - MIT\n",
    "2 - Apache License 2.0\n",
    "3 - GNU General Public License v3.0\n",
    "4 - Creative Commons Attribution 4.0\n",
    "5 - BSD 3-Clause\n",
    "6 - Proprietary\n",
    "7 - None\n",
    "Choose from 1, 2, 3, 4, 5, 6 [1]: \n",
    "Select include_github_actions:\n",
    "1 - no\n",
    "2 - ci\n",
    "3 - ci+cd\n",
    "Choose from 1, 2, 3 [1]:\n",
    "```\n",
    "\n",
    "```{table} A description of the py-pkgs-cookiecutter template prompts.\n",
    ":name: 03-prompt-table\n",
    "|Prompt keyword|Description|\n",
    "|:---  | :---  |\n",
    "|`author_name`, `package_name`, `package_short_description`| These are self-explanatory. Note that we will be publishing our `pycounts` package to Python's main package index PyPI\\index{PyPI}, where names must be unique. **If you plan to follow along with this tutorial you should choose a unique name for your package**. Something like `pycounts_[your intials]` might be appropriate, but you can check if a name is already taken by searching for it on PyPI. We provide guidance on choosing a good package name in **{numref}`04:Package-and-module-names`**. |\n",
    "|`package_version`|The version of your package. Most packages use semantic versioning\\index{semantic versioning}, where a version number consists of three integers `A.B.C`. `A` is the \"major\" version, `B` is the \"minor\" version, and `C` is the \"patch\" version. The first version of a package usually starts at 0.1.0 and increments from there. We'll discuss versioning in **Chapter 7: {ref}`07:Releasing-and-versioning`**.|\n",
    "|`python_version`|The minimum version of Python your package will support. We'll talk more about versions and constraints in **{numref}`03:Dependency-version-constraints`**|\n",
    "|`open_source_license`|The license\\index{license} that dictates how your package can be used by others. We discuss licenses in **{numref}`06:License`**. The MIT license we chose in our example is a permissive license commonly used for open-source work. If your project will not be open source you can choose not to include a license.|\n",
    "|`include_github_actions`|An option to include continuous integration and continuous deployment files for use with GitHub Actions. We'll explore these topics in **Chapter 8: {ref}`08:Continuous-integration-and-deployment`**, so for now, we recommend responding `no`.|\n",
    "```\n",
    "\n",
    "After responding to the `py-pkgs-cookiecutter` prompts, we have a new directory called `pycounts`, full of content suitable for building a fully-featured Python package! We'll explore each element of this directory structure as we develop our `pycounts` package throughout this chapter.\n",
    "\n",
    "\\newpage\n",
    "\n",
    "```md\n",
    "pycounts\n",
    "├── .readthedocs.yml           ┐\n",
    "├── CHANGELOG.md               │\n",
    "├── CONDUCT.md                 │\n",
    "├── CONTRIBUTING.md            │\n",
    "├── docs                       │\n",
    "│   ├── changelog.md           │\n",
    "│   ├── conduct.md             │\n",
    "│   ├── conf.py                │ \n",
    "│   ├── contributing.md        │ Package documentation\n",
    "│   ├── example.ipynb          │\n",
    "│   ├── index.md               │\n",
    "│   ├── make.bat               │\n",
    "│   ├── Makefile               │\n",
    "│   └── requirements.txt       │\n",
    "├── LICENSE                    │\n",
    "├── README.md                  ┘\n",
    "├── pyproject.toml             ┐ \n",
    "├── src                        │\n",
    "│   └── pycounts               │ Package source code, metadata,\n",
    "│       ├── __init__.py        │ and build instructions \n",
    "│       └── pycounts.py        ┘\n",
    "└── tests                      ┐\n",
    "    └── test_pycounts.py       ┘ Package tests\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Put-your-package-under-version-control)=\n",
    "## Put your package under version control"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before continuing to develop our package it is good practice to put it under local and remote version control\\index{version control}. This is not necessary for developing a package, but it is highly recommended so that you can better manage and track changes to your package over time. Version control is particularly useful if you plan on collaborating on your package with others. If you don't want to use version control, feel free to skip to **{numref}`03:Packaging-your-code`**. The tools we will be using for version control in this book are Git\\index{Git} and GitHub\\index{GitHub} (which we set up in **{numref}`02:Set-up-Git-and-GitHub`**). \n",
    "\n",
    "\\newpage\n",
    "\n",
    "```{attention}\n",
    "For this book, we assume readers have basic familiarity with Git and GitHub (or similar). To learn more about Git and GitHub, we recommend the following resources: [*Happy Git and GitHub for the useR*](https://happygitwithr.com){cite:p}`bryan2021` and [*Research Software Engineering with Python*](https://merely-useful.tech/py-rse/git-cmdline.html){cite:p}`rsep2021`.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Set up local version control"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To set up local version control\\index{version control}, navigate to the root *`pycounts/`* directory and initialize a Git\\index{Git} repository:\n",
    "\n",
    "```\n",
    "$ cd pycounts\n",
    "$ git init\n",
    "```\n",
    "\n",
    "```md\n",
    "Initialized empty Git repository in /Users/tomasbeuzen/pycounts/.git/\n",
    "```\n",
    "\n",
    "Next, we need to tell Git which files to track for version control (which will be all of them at this point) and then commit these changes locally:\n",
    "\n",
    "```\n",
    "$ git add .\n",
    "$ git commit -m \"initial package setup\"\n",
    "```\n",
    "\n",
    "```md\n",
    "[master (root-commit) 51795ad] initial package setup\n",
    " 20 files changed, 502 insertions(+)\n",
    " create mode 100644 .gitignore\n",
    " create mode 100644 .readthedocs.yml\n",
    " create mode 100644 CHANGELOG.md\n",
    " ...\n",
    " create mode 100644 src/pycounts/__init__.py\n",
    " create mode 100644 src/pycounts/pycounts.py\n",
    " create mode 100644 tests/test_pycounts.py\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Set up remote version control"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now that we have set up local version control\\index{version control}, we will create a repository on [GitHub](https://github.com/)\\index{GitHub} and set that as the remote version control home for this project. First, we need to create a new repository on [GitHub](https://www.github.com) as demonstrated in {numref}`03-set-up-github-1-fig`:\n",
    "\n",
    "```{figure} images/03-set-up-github-1.png\n",
    "---\n",
    "width: 100%\n",
    "name: 03-set-up-github-1-fig\n",
    "alt: Creating a new repository in GitHub.\n",
    "---\n",
    "Creating a new repository in GitHub.\n",
    "```\n",
    "\n",
    "Next, select the following options when setting up your GitHub repository, as shown in {numref}`03-set-up-github-2-fig`: \n",
    "\n",
    "1. Give the GitHub repository the same name as your Python package and give it a short description.\n",
    "2. You can choose to make your repository public or private — we'll be making ours public so we can share it with others.\n",
    "3. Do not initialize the repository with any files (we've already created all our files locally using the `py-pkgs-cookiecutter` template).\n",
    "\n",
    "```{figure} images/03-set-up-github-2.png\n",
    "---\n",
    "width: 100%\n",
    "name: 03-set-up-github-2-fig\n",
    "alt: Setting up a new repository in GitHub.\n",
    "---\n",
    "Setting up a new repository in GitHub.\n",
    "```\n",
    "\n",
    "Now, use the commands shown on GitHub, and outlined in {numref}`03-set-up-github-3-fig`, to link your local and remote repositories and push your local content to GitHub:\n",
    "\n",
    "```{figure} images/03-set-up-github-3.png\n",
    "---\n",
    "width: 100%\n",
    "name: 03-set-up-github-3-fig\n",
    "alt: Instructions on how to link local and remote version control repositories.\n",
    "---\n",
    "Instructions on how to link local and remote version control repositories.\n",
    "```\n",
    "\n",
    "\\newpage\n",
    "```{attention}\n",
    "The commands below should be specific to your GitHub username and the name of your Python package. They use SSH authentication to connect to GitHub which you will need to set up by following the steps in the official GitHub [documentation](https://docs.github.com/en/authentication/connecting-to-github-with-ssh).\n",
    "```\n",
    "\n",
    "```\n",
    "$ git remote add origin git@github.com:TomasBeuzen/pycounts.git\n",
    "$ git branch -M main\n",
    "$ git push -u origin main\n",
    "```\n",
    "\n",
    "```md\n",
    "Enumerating objects: 26, done.\n",
    "Counting objects: 100% (26/26), done.\n",
    "Delta compression using up to 8 threads\n",
    "Compressing objects: 100% (19/19), done.\n",
    "Writing objects: 100% (26/26), 8.03 KiB | 4.01 MiB/s, done.\n",
    "Total 26 (delta 0), reused 0 (delta 0)\n",
    "To github.com:TomasBeuzen/pycounts.git\n",
    " * [new branch]      main -> main\n",
    "Branch 'main' set up to track remote branch 'main' from 'origin'.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Packaging-your-code)=\n",
    "## Packaging your code"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We now have our `pycounts` package structure\\index{package structure} set up, and are ready to populate our package with the `load_text()`, `clean_text()` and `count_words()` functions we developed at the beginning of the chapter in **{numref}`03:Turning-our-code-into-functions`**. Where should we put these functions? Let's review the structure of our package:\n",
    "\n",
    "\\newpage\n",
    "\n",
    "```md\n",
    "pycounts\n",
    "├── .readthedocs.yml\n",
    "├── CHANGELOG.md\n",
    "├── CONDUCT.md\n",
    "├── CONTRIBUTING.md\n",
    "├── docs\n",
    "│   └── ...\n",
    "├── LICENSE\n",
    "├── pyproject.toml\n",
    "├── README.md\n",
    "├── src\n",
    "│   └── pycounts\n",
    "│       ├── __init__.py\n",
    "│       └── pycounts.py\n",
    "└── tests\n",
    "    └── ...\n",
    "```\n",
    "\n",
    "The Python code for our package should live in modules\\index{module} in the *`src/pycounts/`* directory. The `py-pkgs-cookiecutter` template already created a Python module for us to put our code in called *`src/pycounts/pycounts.py`* (note that this module can be named anything, but it is common for a module to share the name of the package). We'll copy the functions we created in **{numref}`03:Turning-our-code-into-functions`** to the module *`src/pycounts/pycounts.py`* now. Our functions depends on `collections.Counter` and `string.punctuation`, so we also need to import those at the top of the file. Here's what *`src/pycounts/pycounts.py`* should now look like:\n",
    "\n",
    "\\newpage\n",
    "\n",
    "```python\n",
    "from collections import Counter\n",
    "from string import punctuation\n",
    "\n",
    "\n",
    "def load_text(input_file):\n",
    "    \"\"\"Load text from a text file and return as a string.\"\"\"\n",
    "    with open(input_file, \"r\") as file:\n",
    "        text = file.read()\n",
    "    return text\n",
    "    \n",
    "def clean_text(text):\n",
    "    \"\"\"Lowercase and remove punctuation from a string.\"\"\"\n",
    "    text = text.lower()\n",
    "    for p in punctuation:\n",
    "        text = text.replace(p, \"\")\n",
    "    return text\n",
    "    \n",
    "def count_words(input_file):\n",
    "    \"\"\"Count unique words in a string.\"\"\"\n",
    "    text = load_text(input_file)\n",
    "    text = clean_text(text)\n",
    "    words = text.split()\n",
    "    return Counter(words)\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Test drive your package code"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Create-a-virtual-environment)=\n",
    "### Create a virtual environment"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Before we install and test our package, it is highly recommended to set up a virtual environment\\index{virtual environment}. As discussed previously in **{numref}`02:Installing-Python`**, a virtual environment provides a safe and isolated space to develop and install packages. If you don't want to use a virtual environment, feel free to skip to **{numref}`03:Installing-your-package`**.\n",
    "\n",
    "There are several options available when it comes to creating and managing virtual environments (e.g., `conda`\\index{conda} or `venv`). We will use `conda` (which we installed in **{numref}`02:Installing-Python`**) because it is a simple, commonly used, and effective tool for managing virtual environments.\n",
    "\n",
    "To use `conda` to create a new virtual environment called `pycounts` that contains Python, run the following in your terminal:\n",
    "\n",
    "```\n",
    "$ conda create --name pycounts python=3.9 -y\n",
    "```\n",
    "\n",
    "```{note}\n",
    "We are specifying `python=3.9` because that is the minimum version of Python we specified that our package will support in **{numref}`03:Creating-a-package-structure`**.\n",
    "```\n",
    "\n",
    "To use this new environment for developing and installing software we need to \"activate\" it:\n",
    "\n",
    "```\n",
    "$ conda activate pycounts\n",
    "```\n",
    "\n",
    "In most command lines, `conda` will add a prefix like `(pycounts)` to your command-line prompt to indicate which environment you are working in. Anytime you wish to work on your package, you should activate its virtual environment. You can view the packages currently installed in a `conda` environment using the command `conda list`, and you can exit a `conda` virtual environment using `conda deactivate`.\n",
    "\n",
    "```{note}\n",
    "`poetry`, the packaging tool we'll use to develop our package later in this chapter, also supports [virtual environment management](https://python-poetry.org/docs/managing-environments/) without the need for `conda`. However, we find `conda` to be a more intuitive and explicit environment manager, which is why we advocate for it in this book. \n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Installing-your-package)=\n",
    "### Installing your package"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We have our package structure set up and we've populated it with our Python code. How do we install and use our package? There are several tools available to develop installable Python packages\\index{installable Python package}. The most common are `poetry`\\index{poetry}, `flit`\\index{flit}, and `setuptools`\\index{setuptools}, which we compare in **{numref}`04:Packaging-tools`**. In this book, we will be using `poetry` (which we installed in **{numref}`02:Install-packaging-software`**); it is a modern packaging tool that provides simple and efficient commands to develop, install, and distribute Python packages.\n",
    "\n",
    "In a `poetry`-managed package, the *`pyproject.toml`*\\index{pyproject.toml} file stores all the metadata and install instructions for the package. The *`pyproject.toml`* that the `py-pkgs-cookiecutter` created for our `pycounts` package looks like this:\n",
    "\n",
    "```toml\n",
    "[tool.poetry]\n",
    "name = \"pycounts\"\n",
    "version = \"0.1.0\"\n",
    "description = \"Calculate word counts in a text file.\"\n",
    "authors = [\"Tomas Beuzen\"]\n",
    "license = \"MIT\"\n",
    "readme = \"README.md\"\n",
    "\n",
    "[tool.poetry.dependencies]\n",
    "python = \"^3.9\"\n",
    "\n",
    "[tool.poetry.dev-dependencies]\n",
    "\n",
    "[build-system]\n",
    "requires = [\"poetry-core>=1.0.0\"]\n",
    "build-backend = \"poetry.core.masonry.api\"\n",
    "```\n",
    "\n",
    "{numref}`03-toml-table` provides a brief description of each of the headings in that file (called \"tables\" in TOML file jargon).\n",
    "\n",
    "```{table} A description of the tables in the pyproject.toml.\n",
    ":name: 03-toml-table\n",
    "|TOML table|Description|\n",
    "|:---  | :---  |\n",
    "|`[tool.poetry]`|Defines package metadata. The `name`, `version`, `description`, and `authors` of the package are required.|\n",
    "|`[tool.poetry.dependencies]`|Identifies dependencies\\index{dependency} of a package — that is, software that the package depends on. Our `pycounts` package only depends on Python 3.9 or higher, but we'll add other dependencies to our package later in this chapter.|\n",
    "|`[tool.poetry.dev-dependencies]`|Identifies development dependencies\\index{dependency!development dependency} of a package — dependencies required for development purposes, such as running tests or building documentation. We'll add development dependencies to our `pycounts` package later in this chapter.|\n",
    "|`[build-system]`|Identifies the build tools required to build your package. We'll talk more about this in **{numref}`03:Building-and-distributing-your-package`**.|\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With our *`pyproject.toml`* file already set up for us by the `py-pkgs-cookiecutter` template, we can use `poetry` to install our package using the command `poetry install` at the command line from the root package directory:\n",
    "\n",
    "```\n",
    "$ poetry install\n",
    "```\n",
    "\n",
    "```md\n",
    "Updating dependencies\n",
    "Resolving dependencies... (0.1s)\n",
    "\n",
    "Writing lock file\n",
    "\n",
    "Installing the current project: pycounts (0.1.0)\n",
    "```\n",
    "\n",
    "```{tip}\n",
    "When you run `poetry install`, `poetry` creates a *`poetry.lock`* file, which contains a record of all the dependencies you've installed while developing your package. For anyone else working on your project (including you in the future), running `poetry install` installs dependencies from *`poetry.lock`* to ensure that they have the same versions of dependencies that you did when developing the package. We won't be focusing on *`poetry.lock`* in this book, but it can be a helpful development tool, which you can read more about in the `poetry` [documentation](https://python-poetry.org/docs/basic-usage/#installing-dependencies). \n",
    "```\n",
    "\n",
    "With our package installed, we can now `import` and use it in a Python session. Before we do that, we need a text file to test our package on. Feel free to use any text file, but we'll create the same \"Zen of Python\" text file we used earlier in the chapter by running the following at the command line:\n",
    "\n",
    "```\n",
    "$ python -c \"import this\" > zen.txt\n",
    "```\n",
    "\n",
    "Now we can open a Python interpreter and `import`\\index{import} and use the `count_words()` function from our `pycounts` module with the following code:\n",
    "\n",
    "```\n",
    ">>> from pycounts.pycounts import count_words\n",
    ">>> count_words(\"zen.txt\")\n",
    "```\n",
    "\n",
    "```md\n",
    "Counter({'is': 10, 'better': 8, 'than': 8, 'the': 6, \n",
    "'to': 5, 'of': 3, 'although': 3, 'never': 3, ... })\n",
    "```\n",
    "\n",
    "Looks like everything is working! We have now created and installed a simple Python package! You can now use this Python package in any project you wish (if using virtual environments, you'll need to `poetry install` the package in them before it can be used).\n",
    "\n",
    "`poetry install` actually installs packages in \"editable mode\", which means that it installs a link to your package's code on your computer (rather than installing it as a independent piece of software). Editable installs\\index{editable install} are commonly used by developers because it means that any edits made to the package's source code are immediately available the next time it is imported, without having to `poetry install` again. We'll talk more about installing packages in **{numref}`03:Building-and-distributing-your-package`**.\n",
    "\n",
    "In the next section, we'll show how to add code to our package that depends on another package. But for those using version control, it's a good idea to commit the changes we've made to *`src/pycounts/pycounts.py`* to local and remote version control\\index{version control}:\n",
    "\n",
    "```\n",
    "$ git add src/pycounts/pycounts.py\n",
    "$ git commit -m \"feat: add word counting functions\"\n",
    "$ git push\n",
    "```\n",
    "\n",
    "\\newpage\n",
    "\n",
    "```{tip}\n",
    "In this book, we use the [Angular style](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#-git-commit-guidelines) for Git commit messages. We'll talk about this style more in **{numref}`07:Automatic-version-bumping`**, but our commit messages have the form \"type: subject\", where \"type\" indicates the kind of change being made and \"subject\" contains a description of the change. We'll be using the following \"types\" for our commits:\n",
    "\n",
    "- \"build\": indicates a change to the build system or external dependencies.\n",
    "- \"docs\": indicates a change to documentation.\n",
    "- \"feat\": indicates a new feature being added to the code base.\n",
    "- \"fix\": indicates a bug fix.\n",
    "- \"test\": indicates changes to testing framework.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Adding-dependencies-to-your-package)=\n",
    "## Adding dependencies to your package"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's now add a new function to our package that can plot a bar chart of the top `n` words in a `Counter` object of word counts. Imagine we've come up with the following `plot_words()` function that does this. The function uses the convenient `.most_common()` method of the `Counter` object to return a list of tuples of the top `n` words counts in the format `(word, count)`. It then uses the Python function `zip(*...)` to unpack that list of tuples into two individual lists, `word` and `count`. Finally, the `matplotlib` {cite:p}`hunter2007` package is used to plot the result (`plt.bar(...)`), which looks like {numref}`03-matplotlib-figure-fig`.\n",
    "\n",
    "```{note}\n",
    "If this code is not familiar to you, don't worry! The code itself is not overly important to our discussion of packaging. You just need to know that we are adding some new code to our package that depends on the `matplotlib` package.\n",
    "```\n",
    "\n",
    "\\newpage\n",
    "\n",
    "```python\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "def plot_words(word_counts, n=10):\n",
    "    \"\"\"Plot a bar chart of word counts.\"\"\"\n",
    "    top_n_words = word_counts.most_common(n)\n",
    "    word, count = zip(*top_n_words)\n",
    "    fig = plt.bar(range(n), count)\n",
    "    plt.xticks(range(n), labels=word, rotation=45)\n",
    "    plt.xlabel(\"Word\")\n",
    "    plt.ylabel(\"Count\")\n",
    "    return fig\n",
    "```\n",
    "\n",
    "```{figure} images/03-matplotlib-figure.png\n",
    "---\n",
    "width: 80%\n",
    "name: 03-matplotlib-figure-fig\n",
    "alt: Example figure created from the plotting function.\n",
    "---\n",
    "Example figure created from the plotting function.\n",
    "```\n",
    "\n",
    "Where should we put this function in our package? You could certainly add all your package code into a single module (e.g., *`src/pycounts/pycounts.py`*), but as you add functionality to your package that module will quickly become overcrowded and hard to manage. Instead, as you write more code, it's a good idea to organize it into multiple, logical modules. With that in mind, we'll create a new module called *`src/pycounts/plotting.py`* to house our plotting function `plot_words()`. Create that new module now in an editor of your choice.\n",
    "\n",
    "Your package structure\\index{package structure} should now look like this:\n",
    "\n",
    "```{code-block} md\n",
    "---\n",
    "emphasize-lines: 15 \n",
    "---\n",
    "pycounts\n",
    "├── .readthedocs.yml\n",
    "├── CHANGELOG.md\n",
    "├── CONDUCT.md\n",
    "├── CONTRIBUTING.md\n",
    "├── docs\n",
    "│   └── ...\n",
    "├── LICENSE\n",
    "├── poetry.lock\n",
    "├── pyproject.toml\n",
    "├── README.md\n",
    "├── src\n",
    "│   └── pycounts\n",
    "│       ├── __init__.py\n",
    "│       ├── plotting.py  <--------\n",
    "│       └── pycounts.py\n",
    "└── tests\n",
    "    └── ...\n",
    "```\n",
    "\n",
    "Open *`src/pycounts/plotting.py`* and add the `plot_words()` code from above (don't forget to add the `import matplotlib.pyplot as plt` at the top of the module).\n",
    "\n",
    "After doing this, if we tried to `import` our new function in a Python interpreter we'd get an error:\n",
    "\n",
    "```{attention}\n",
    "If using a `conda` virtual environment, make sure that environment is active by running `conda activate pycounts`, before using or working on your package.\n",
    "```\n",
    "\n",
    "```\n",
    ">>> from pycounts.plotting import plot_words\n",
    "```\n",
    "\n",
    "```md\n",
    "ModuleNotFoundError: No module named 'matplotlib'\n",
    "```\n",
    "\n",
    "This is because `matplotlib` is not part of the standard Python library; we need to install it and add it as a dependency\\index{dependency} of our `pycounts` package. We can do this with `poetry` using the command `poetry add`. This command will install the specified dependency into the current virtual environment\\index{virtual environment} and will update the `[tool.poetry.dependencies]` section of the *`pyproject.toml`*\\index{pyproject.toml} file:\n",
    "\n",
    "```\n",
    "$ poetry add matplotlib\n",
    "``` \n",
    "\n",
    "```md\n",
    "Using version ^3.4.3 for matplotlib\n",
    "\n",
    "Updating dependencies\n",
    "Resolving dependencies...\n",
    "\n",
    "Writing lock file\n",
    "\n",
    "Package operations: 8 installs, 0 updates, 0 removals\n",
    "\n",
    "  • Installing six (1.16.0)\n",
    "  • Installing cycler (0.10.0)\n",
    "  • Installing kiwisolver (1.3.1)\n",
    "  • Installing numpy (1.21.1)\n",
    "  • Installing pillow (8.3.1)\n",
    "  • Installing pyparsing (2.4.7)\n",
    "  • Installing python-dateutil (2.8.2)\n",
    "  • Installing matplotlib (3.4.3)\n",
    "```\n",
    "\n",
    "If you open *`pyproject.toml`* file, you should now see `matplotlib` listed as a dependency under the `[tool.poetry.dependencies]` section (which previously only contained Python 3.9 as a dependency, as we saw in **{numref}`03:Installing-your-package`**):\n",
    "\n",
    "```toml\n",
    "[tool.poetry.dependencies]\n",
    "python = \"^3.9\"\n",
    "matplotlib = \"^3.4.3\"\n",
    "```\n",
    "\n",
    "We can now use our package in a Python interpreter as follows (be sure that the *`zen.txt`* file we created earlier is in the current directory if you're running the code below):\n",
    "\n",
    "```\n",
    ">>> from pycounts.pycounts import count_words\n",
    ">>> from pycounts.plotting import plot_words\n",
    ">>> counts = count_words(\"zen.txt\")\n",
    ">>> fig = plot_words(counts, 10)\n",
    "```\n",
    "\n",
    "If running the above Python code in an interactive IPython shell or Jupyter Notebook, the plot will be displayed automatically. If you're running from the Python interpreter, you'll need to run the `matplotlib` command `plt.show()` to display the plot, as shown below:\n",
    "\n",
    "```\n",
    ">>> import matplotlib.pyplot as plt\n",
    ">>> plt.show()\n",
    "```\n",
    "\n",
    "We've made some important changes to our package in this section by adding a new module and a dependency. Those using version control\\index{version control} should commit these changes:\n",
    "\n",
    "```\n",
    "$ git add src/pycounts/plotting.py\n",
    "$ git commit -m \"feat: add plotting module\"\n",
    "$ git add pyproject.toml poetry.lock\n",
    "$ git commit -m \"build: add matplotlib as a dependency\"\n",
    "$ git push\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Dependency-version-constraints)=\n",
    "### Dependency version constraints"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Versioning is the practice of assigning a unique identifier to unique releases of a package. For example, [semantic versioning](https://semver.org)\\index{semantic versioning} is a common versioning system that consists of three integers `A.B.C`. `A` is the \"major\" version, `B` is the \"minor\" version, and `C` is the \"patch\" version identifier. Package versions usually starts at 0.1.0 and positively increment the major, minor, and patch numbers from there, depending on the kind of changes made to the package over time.\n",
    "\n",
    "We'll talk more about versioning in **Chapter 7: {ref}`07:Releasing-and-versioning`**, but what's important to know now is that we typically constrain the required version number(s) of our package's dependencies, to ensure we're using versions that are up-to-date and contain the functionality we need. You may have noticed `poetry` prepended a caret (^) operator to the dependency versions in our *`pyproject.toml`* file, under the `[tool.poetry.dependencies]` section:\n",
    "\n",
    "```toml\n",
    "[tool.poetry.dependencies]\n",
    "python = \"^3.9\"\n",
    "matplotlib = \"^3.4.3\"\n",
    "```\n",
    "\n",
    "The caret operator is short-hand for \"requires this or any higher version that does not modify the left-most non-zero version digit\". For example, our package depends on any Python version >=3.9.0 and <4.0.0. Thus, examples of valid versions include 3.9.1 and 3.12.0, but 4.0.1 would be invalid. There are many other syntaxes that can be used to specify version constraints in different ways, as you can read more about in the `poetry` [documentation](https://python-poetry.org/docs/dependency-specification). So why do we care about this? The caret operator enforces an upper cap on the dependency versions our package requires. A problem with this approach is that it forces anyone depending on your package to specify the same constraints and can thus make it difficult to add and resolve dependencies.\n",
    "\n",
    "This problem is best shown by example. Version 1.21.5 of the popular `numpy`{cite:p}`harris2020array` package had bound version constraints on Python, requiring version >=3.7 and <3.11 (see the [source code](https://github.com/numpy/numpy/blob/c3d0a09342c08c466984654bc4738af595fba896/setup.py#L409)). Watch what happens if we try to add this version of `numpy` to our `pycounts` package (we use the argument `--dry-run` to show what would happen here without actually executing anything):\n",
    "\n",
    "```\n",
    "$ poetry add numpy=1.21.5 --dry-run\n",
    "``` \n",
    "\n",
    "```md\n",
    "Updating dependencies\n",
    "Resolving dependencies... (0.1s)\n",
    "\n",
    "SolverProblemError\n",
    "\n",
    "The current project's Python requirement (>=3.9,<4.0) is not compatible \n",
    "with some of the required packages Python requirement:\n",
    "    - numpy requires Python >=3.7,<3.11, so it will not be satisfied \n",
    "      for Python >=3.11,<4.0\n",
    "```\n",
    "\n",
    "The problem here is that our package currently supports Python versions ^3.9 (i.e., >=3.9.0 and <4.0.0), so if we released it, a user with Python 3.12.0 would technically be able to install it. However, `numpy` 1.21.5 only supports >=3.7 and <3.11 which would not be compatible with Python 3.12.0 (or any version >=3.11). As a result of this inconsistency, `poetry` refuses to add `numpy` 1.21.5 as a dependency of our package. To add it, we have three main choices:\n",
    "\n",
    "1. Change the Python version constraints of our package to >=3.7 and <3.11.\n",
    "2. Wait for a version of `numpy` that is compatible with our package's Python constraints.\n",
    "3. Manually specify the versions of Python for which the dependency can be installed, e.g.: `poetry add numpy=1.21.5 --python \">=3.7, <3.11\"`.  \n",
    "\n",
    "None of these options is really ideal, especially if your package has a large number of dependencies with different bound version constraints. However, a simple way this issue could be resolved is if `numpy` 1.21.5 did not having an upper cap on the Python version required. In fact, in the subsequent minor version release of `numpy`, 1.22.0, the upper version cap on Python was removed, requiring only version >=3.8 (see the [source code](https://github.com/numpy/numpy/blob/4adc87dff15a247e417d50f10cc4def8e1c17a03/setup.py#L410)), which we would be able to successfully add to our package:\n",
    "\n",
    "```\n",
    "$ poetry add numpy=1.22.0 --dry-run\n",
    "``` \n",
    "\n",
    "Ultimately, version constraints are an important issue that can affect the usability of your package. If you intend to share your package, having an upper cap on dependency versions can make it very difficult for other developers to use your package as a dependency in their own projects. At the time of writing, much of the packaging community, including the [Python Packaging Authority](https://github.com/pypa/packaging.python.org/pull/850), generally recommend not using an upper cap on version constraints unless absolutely necessary. As a result, we recommend specifying version constraints without an upper cap by manually changing `poetry`'s default caret operator (^) to a greater-than-or-equal-to sign (>=). For example, we will change the `[tool.poetry.dependencies]` section of our *`pyproject.toml`* file as follows:\n",
    "\n",
    "```toml\n",
    "[tool.poetry.dependencies]\n",
    "python = \">=3.9\"\n",
    "matplotlib = \">=3.4.3\"\n",
    "```\n",
    "\n",
    "You can read more about the issues around version constraints, as well as examples where they might actually be valid, in Henry Schreiner's excellent [blog post](https://iscinumpy.dev/post/bound-version-constraints/). Those using version control should commit this import change we've made to our package:\n",
    "\n",
    "```\n",
    "$ git add pyproject.toml\n",
    "$ git commit -m \"build: remove upper bound on dependency versions\"\n",
    "$ git push\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Testing-your-package)=\n",
    "## Testing your package"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Writing-tests)=\n",
    "### Writing tests"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "At this point we have developed a package that can count words in a text file and plot the results. But how can we be certain that our package works correctly and produces reliable results?\n",
    "\n",
    "One thing we can do is write tests\\index{tests} for our package that check the package is working as expected. This is particularly important if you intend to share your package with others (you don't want to share code that doesn't work!). But even if you don't intend to share your package, writing tests can still be helpful to catch errors in your code and to write new code without breaking any tried-and-tested existing functionality. If you don't want to write to tests for your package feel free to skip to **{numref}`03:Package-documentation`**.\n",
    "\n",
    "Many of us already conduct informal tests of our code by running it a few times in a Python session to see if it's working as we expect, and if not, changing the code and repeating the process. This is called \"manual testing\" or \"exploratory testing\". However, when writing software, it's preferable to define your tests in a more formal and reproducible way.\n",
    "\n",
    "Tests in Python are often written with the `assert`\\index{tests!assert} statement. `assert` checks the truth of an expression; if the expression is true, Python does nothing and continues running, but if it's false, the code terminates and shows a user-defined error message. For example, consider running the follow code in a Python interpreter:\n",
    "\n",
    "```\n",
    ">>> ages = [32, 19, 9, 75]\n",
    ">>> for age in ages:\n",
    ">>>     assert age >= 18, \"Person is younger than 18!\"\n",
    ">>>     print(\"Age verified!\")\n",
    "```\n",
    "\n",
    "```md\n",
    "Age verified!\n",
    "Age verified!\n",
    "Traceback (most recent call last):\n",
    "  File \"<stdin>\", line 2, in <module>\n",
    "AssertionError: Person is younger than 18!\n",
    "```\n",
    "\n",
    "Note how the first two \"ages\" (32 and 19) are verified, with an \"Age verified!\" message printed to screen. But the third age of 9 fails the `assert`, so an error message is raised and the program terminates before checking the last age of 75.\n",
    "\n",
    "Using the `assert` statement, let's write a test for the `count_words()` function of our `pycounts` package. There are different kinds of tests used to test software (unit tests, integration tests, regression tests, etc.); we discuss these in **Chapter 5: {ref}`05:Testing`**. For now, we'll write a unit test\\index{tests!unit test}. Unit tests evaluate a single \"unit\" of software, such as a Python function, to check that it produces an expected result. A unit test consists of:\n",
    "\n",
    "1. Some data to test the code with (called a \"*fixture\\index{tests!fixture}*\"). The fixture is typically a small or simple version of the data the function will typically process.\n",
    "2. The *actual* result that the code produces given the fixture.\n",
    "3. The *expected* result of the test, which is compared to the *actual* result using an `assert` statement.\n",
    "\n",
    "The unit test we are going to write will `assert` that the `count_words()` function produces an expected result given a certain fixture. We'll use the following quote from Albert Einstein as our fixture:\n",
    "\n",
    ">*\"Insanity is doing the same thing over and over and expecting different results.\"*\n",
    "\n",
    "The *actual* result is the result `count_words()` outputs when we input this fixture. We can get the *expected* result by manually counting the words in the quote (ignoring capitalization and punctuation):\n",
    "\n",
    "```python\n",
    "einstein_counts = {'insanity': 1, 'is': 1, 'doing': 1, \n",
    "                   'the': 1, 'same': 1, 'thing': 1, \n",
    "                   'over': 2, 'and': 2, 'expecting': 1,\n",
    "                   'different': 1, 'results': 1}\n",
    "```\n",
    "\n",
    "To write our unit test in Python code, let's first create a text file containing the Einstein quote to use as our fixture. We'll add it to the *`tests/`* directory of our package as a file called *`einstein.txt`* — you can make the file manually, or you can create it from a Python session started in the root package directory using the following code:\n",
    "\n",
    "```\n",
    ">>> quote = \"Insanity is doing the same thing over and over and \\\n",
    "             expecting different results.\"\n",
    ">>> with open(\"tests/einstein.txt\", \"w\") as file:\n",
    "        file.write(quote)\n",
    "```\n",
    "\n",
    "Now, a unit test for our `count_words()` function would look as below:\n",
    "\n",
    "```\n",
    ">>> from pycounts.pycounts import count_words\n",
    ">>> from collections import Counter\n",
    ">>> expected = Counter({'insanity': 1, 'is': 1, 'doing': 1, \n",
    "                        'the': 1, 'same': 1, 'thing': 1, \n",
    "                        'over': 2, 'and': 2, 'expecting': 1,\n",
    "                        'different': 1, 'results': 1})\n",
    ">>> actual = count_words(\"tests/einstein.txt\")\n",
    ">>> assert actual == expected, \"Einstein quote counted incorrectly!\"\n",
    "```\n",
    "\n",
    "If the above code runs without error, our `count_words()` function is working, at least to our test specifications. In the next section, we'll discuss how we can make this testing process more efficient."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Running-tests)=\n",
    "### Running tests"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It would be tedious and inefficient to manually write and execute unit tests\\index{tests} for your package's code like we did above. Instead, it's common to use a \"testing framework\" to automatically run our tests for us. `pytest`\\index{tests!pytest} is the most common test framework\\index{tests!test framework} used for Python packages. To use `pytest`:\n",
    "\n",
    "1. Tests are defined as functions prefixed with `test_` and contain one or more statements that `assert` code produces an expected result.\n",
    "2. Tests are put in files of the form *`test_*.py`* or *`*_test.py`*, and are usually placed in a directory called *`tests/`* in a package's root.\n",
    "3. Tests can be executed using the command `pytest` at the command line and pointing it to the directory your tests live in (i.e., `pytest tests/`). `pytest` will find all files of the form *`test_*.py`* or *`*_test.py`* in that directory and its sub-directories, and execute any functions with names prefixed with `test_`.\n",
    "\n",
    "The `py-pkgs-cookiecutter`\\index{py-pkgs-cookiecutter} created a *`tests/`* directory and a module called *`test_pycounts.py`* for us to put our tests in:\n",
    "\n",
    "\\newpage\n",
    "\n",
    "```{code-block} md\n",
    "---\n",
    "emphasize-lines: 13-15\n",
    "---\n",
    "pycounts\n",
    "├── CHANGELOG.md\n",
    "├── CONDUCT.md\n",
    "├── CONTRIBUTING.md\n",
    "├── docs\n",
    "│   └── ...\n",
    "├── LICENSE\n",
    "├── poetry.lock\n",
    "├── pyproject.toml\n",
    "├── README.md\n",
    "├── src\n",
    "│   └── ...\n",
    "└── tests                 <--------\n",
    "    ├── einstein.txt      <--------\n",
    "    └── test_pycounts.py  <--------\n",
    "```\n",
    "\n",
    "```{note}\n",
    "We created the file *`tests/einstein.txt`* ourselves in **{numref}`03:Writing-tests`**, it was not created by the `py-pkgs-cookiecutter`.\n",
    "```\n",
    "\n",
    "As mentioned above, `pytest` tests are written as functions prefixed with `test_` and which contain one or more `assert` statements that check some code functionality. Based on this format, let's add the unit test we created in **{numref}`03:Writing-tests`** as a test function to *`tests/test_pycounts.py`* using the below Python code:\n",
    "\n",
    "\n",
    "```python\n",
    "from pycounts.pycounts import count_words\n",
    "from collections import Counter\n",
    "\n",
    "def test_count_words():\n",
    "    \"\"\"Test word counting from a file.\"\"\"\n",
    "    expected = Counter({'insanity': 1, 'is': 1, 'doing': 1, \n",
    "                        'the': 1, 'same': 1, 'thing': 1, \n",
    "                        'over': 2, 'and': 2, 'expecting': 1,\n",
    "                        'different': 1, 'results': 1})\n",
    "    actual = count_words(\"tests/einstein.txt\")\n",
    "    assert actual == expected, \"Einstein quote counted incorrectly!\"\n",
    "```\n",
    "\n",
    "\\newpage\n",
    "\n",
    "Before we can use `pytest` to run our test for us we need to add it as a development dependency\\index{dependency!development dependency} of our package using the command `poetry add --group dev`. A development dependency is a package that is not required by a user to use your package but is required for development purposes (like testing):\n",
    "\n",
    "```{attention}\n",
    "If using a `conda` virtual environment, make sure that environment is active by running `conda activate pycounts`, before using or working on your package.\n",
    "```\n",
    "\n",
    "```\n",
    "$ poetry add --group dev pytest\n",
    "```\n",
    "\n",
    "If you look in the *`pyproject.toml`* file you will see that `pytest` gets added under the `[tool.poetry.group.dev.dependencies]` section (which was previously empty, as we saw in **{numref}`03:Installing-your-package`**):\n",
    "\n",
    "```toml\n",
    "[tool.poetry.group.dev.dependencies]\n",
    "pytest = \"^6.2.5\"\n",
    "```\n",
    "\n",
    "To use `pytest` to run our test we can use the following command from our root package directory:\n",
    "\n",
    "```\n",
    "$ pytest tests/\n",
    "```\n",
    "\n",
    "```md\n",
    "========================= test session starts =========================\n",
    "...\n",
    "collected 1 item                                                                                            \n",
    "\n",
    "tests/test_pycounts.py .                                         [100%]\n",
    "\n",
    "========================== 1 passed in 0.01s ==========================\n",
    "```\n",
    "\n",
    "\\newpage\n",
    "\n",
    "```{attention}\n",
    "If you're not developing your package in a `conda` virtual environment, `poetry` will automatically create a virtual environment for you using a tool called `venv` (read more in the [documentation](https://python-poetry.org/docs/managing-environments/)). You'll need to tell `poetry` to use this environment by prepending any command you run with `poetry run`, like: `poetry run pytest tests/`. \n",
    "```\n",
    "\n",
    "From the `pytest` output we can see that our test passed! At this point, we could add more tests for our package by writing more `test_*` functions. But we'll do this in **Chapter 5: {ref}`05:Testing`**. Typically you want to write enough tests to check all the core code of your package. We'll show how you can calculate how much of your package's code your tests actually check in the next section."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Code-coverage)=\n",
    "### Code coverage"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A good test suite will contain tests that check as much of your package's code as possible. How much of your code your tests actually use is called \"code coverage\\index{code coverage}\". The simplest and most intuitive measure of code coverage is line coverage\\index{code coverage!line coverage}. It is the proportion of lines of your package's code that are executed by your tests\\index{tests}:\n",
    "\n",
    "$$\n",
    "  \\text{coverage} = \\frac{\\text{lines executed}}{\\text{total lines}} * 100\\%\n",
    "$$\n",
    "\n",
    "There is a useful extension to `pytest`\\index{tests!pytest} called `pytest-cov`\\index{tests!pytest-cov}, which we can use to calculate coverage. First, we'll use `poetry` to add `pytest-cov` as a development dependency\\index{dependency!development dependency} of our `pycounts` package:\n",
    "\n",
    "```\n",
    "$ poetry add --group dev pytest-cov\n",
    "```\n",
    "\n",
    "We can calculate the line coverage of our tests by running the following command, which tells `pytest-cov` to calculate the coverage our tests have of our `pycounts` package:\n",
    "\n",
    "```\n",
    "$ pytest tests/ --cov=pycounts\n",
    "```\n",
    "\n",
    "\\newpage\n",
    "\n",
    "```md\n",
    "========================= test session starts =========================\n",
    "...\n",
    "\n",
    "Name                       Stmts   Miss  Cover\n",
    "----------------------------------------------\n",
    "src/pycounts/__init__.py       2      0   100%\n",
    "src/pycounts/plotting.py       9      9     0%\n",
    "src/pycounts/pycounts.py      16      0   100%\n",
    "----------------------------------------------\n",
    "TOTAL                         27      9    67%\n",
    "\n",
    "========================== 1 passed in 0.02s ==========================\n",
    "```\n",
    "\n",
    "In the output above, `Stmts` is how many lines are in a module, `Miss` is how many lines were not executed during your tests, and `Cover` is the percentage of lines covered by your tests. From the above output, we can see that our tests currently don't cover any of the lines in the `pycounts.plotting` module. We'll write more tests for our package, and discuss more advanced methods of testing and calculating code coverage in **Chapter 5: {ref}`05:Testing`**.\n",
    "\n",
    "For those using version control, commit the changes we've made to our packages tests to local and remote version control\\index{version control}:\n",
    "\n",
    "```\n",
    "$ git add pyproject.toml poetry.lock\n",
    "$ git commit -m \"build: add pytest and pytest-cov as dev dependencies\"\n",
    "$ git add tests/*\n",
    "$ git commit -m \"test: add unit test for count_words\"\n",
    "$ git push\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Package-documentation)=\n",
    "## Package documentation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Documentation\\index{documentation} describing what your package does and how to use it is invaluable for the users of your package (including yourself). The amount of documentation needed to support a package varies depending on its complexity and the intended audience. A typical package contains documentation in various parts of its directory structure\\index{package structure}, as shown in {numref}`03-documentation-table`. There's a lot here but don't worry, we'll show how to efficiently write all these pieces of documentation in the following sections.\n",
    "\n",
    "```{table} Typical Python package documentation.\n",
    ":name: 03-documentation-table\n",
    "|Documentation|Typical location|Description|\n",
    "|:---    | :--- | :---      |\n",
    "|README\\index{documentation!README}|Root |Provides high-level information about the package, e.g., what it does, how to install it, and how to use it.|\n",
    "|License\\index{documentation!license}|Root |Explains who owns the copyright to your package source and how it can be used and shared.|\n",
    "|Contributing guidelines\\index{documentation!contributing guidelines}|Root |Explains how to contribute to the project.|\n",
    "|Code of conduct\\index{documentation!code of conduct}|Root |Defines standards for how to appropriately engage with and contribute to the project.|\n",
    "|Changelog\\index{documentation!changelog}|Root |A chronologically ordered list of notable changes to the package over time, usually organized by version.|\n",
    "|Docstrings\\index{docstring}| *.py* files |Text appearing as the first statement in a function, method, class, or module in Python that describes what the code does and how to use it. Accessible to users via the `help()` command.|\n",
    "|Examples|*`docs/`* |Step-by-step, tutorial-like examples showing how the package works in more detail.|\n",
    "|Application programming interface\\index{application programming interface} (API) reference|*`docs/`* | An organized list of the user-facing functionality of your package (i.e., functions, classes, etc.) along with a short description of what they do and how to use them. Typically created automatically from your package's docstrings using the `sphinx` tool as we'll discuss in **{numref}`03:Building-documentation`**.|\n",
    "```\n",
    "\n",
    "Our `pycounts` package is a good example of a package with all this documentation:\n",
    "\n",
    "```{code-block} md\n",
    "---\n",
    "emphasize-lines: 3-10\n",
    "---\n",
    "pycounts\n",
    "├── .readthedocs.yml\n",
    "├── CHANGELOG.md\n",
    "├── CONDUCT.md\n",
    "├── CONTRIBUTING.md\n",
    "├── docs\n",
    "│   ├── example.ipynb \n",
    "│   └── ...\n",
    "├── LICENSE\n",
    "├── README.md\n",
    "├── poetry.lock\n",
    "├── pyproject.toml\n",
    "├── src\n",
    "│   └── ...\n",
    "└── tests\n",
    "    └── ...\n",
    "```\n",
    "\n",
    "The typical workflow for documenting a Python package consists of three steps:\n",
    "\n",
    "1. **Write documentation**: manually write documentation in a plain-text format.\n",
    "2. **Build documentation\\index{documentation!build documentation}**: compile and render documentation into HTML using the documentation generator `sphinx`\\index{sphinx}.\n",
    "3. **Host documentation\\index{documentation!host documentation} online**: share the built documentation online so it can be easily accessed by anyone with an internet connection, using a free service like [Read the Docs](https://readthedocs.org)\\index{Read the Docs} or [GitHub Pages](https://pages.github.com)\\index{GitHub Pages}.\n",
    "\n",
    "In this section, we will walk through each of these steps in detail."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Writing-documentation)=\n",
    "### Writing documentation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python package documentation\\index{documentation} is typically written in a plain-text markup format such as [Markdown](https://en.wikipedia.org/wiki/Markdown)\\index{Markdown} (*.md*) or [reStructuredText](https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html)\\index{reStructuredText} (*.rst*). With a plain-text markup language, documents are written in plain-text and a special syntax is used to specify how the text should be formatted when it is rendered by a suitable tool. We'll show an example of this below, but we'll be using the Markdown language in this book because it is widely used, and we feel it has a less verbose and more intuitive syntax than reStructuredText (check out the [Markdown Guide](https://www.markdownguide.org) to learn more about Markdown syntax).\n",
    "\n",
    "Most developers create packages from templates which pre-populate a lot of the standard package documentation for them. For example, as we saw in **{numref}`03:Package-documentation`**, the `py-pkgs-cookiecutter`\\index{py-pkgs-cookiecutter} template we used to create our `pycounts` package created a *`LICENSE`*, *`CHANGELOG.md`*, contributing guidelines (*`CONTRIBUTING.md`*), and code of conduct (*`CONDUCT.md`*) for us already!\n",
    "\n",
    "A *`README.md`* was also created, but it contains a \"Usage\" section, which is currently empty. Now that we've developed the basic functionality of `pycounts`, we can fill that section with Markdown text as follows:\n",
    "\n",
    "```{tip}\n",
    "In the Markdown text below, the following syntax is used:\n",
    "\n",
    "- Headers are denoted with number signs (\\#). The number of number signs corresponds to the heading level.\n",
    "- Code blocks are bounded by three back-ticks. A programming language can succeed the opening bounds to specify how the code syntax should be highlighted.\n",
    "- Links are defined using brackets \\[\\] to enclose the link text, followed by the URL in parentheses ().\n",
    "```\n",
    "\n",
    "````xml\n",
    "# pycounts\n",
    "\n",
    "Calculate word counts in a text file!\n",
    "\n",
    "## Installation\n",
    "\n",
    "```bash\n",
    "$ pip install pycounts\n",
    "```\n",
    "\n",
    "## Usage\n",
    "\n",
    "`pycounts` can be used to count words in a text file and plot results\n",
    "as follows:\n",
    "\n",
    "```python\n",
    "from pycounts.pycounts import count_words\n",
    "from pycounts.plotting import plot_words\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "file_path = \"test.txt\"  # path to your file\n",
    "counts = count_words(file_path)\n",
    "fig = plot_words(counts, n=10)\n",
    "plt.show()\n",
    "```\n",
    "\n",
    "## Contributing\n",
    "\n",
    "Interested in contributing? Check out the contributing guidelines. \n",
    "Please note that this project is released with a Code of Conduct. \n",
    "By contributing to this project, you agree to abide by its terms.\n",
    "\n",
    "## License\n",
    "\n",
    "`pycounts` was created by Tomas Beuzen. It is licensed under the terms\n",
    "of the MIT license.\n",
    "\n",
    "## Credits\n",
    "\n",
    "`pycounts` was created with \n",
    "[`cookiecutter`](https://cookiecutter.readthedocs.io/en/latest/) and \n",
    "the `py-pkgs-cookiecutter` \n",
    "[template](https://github.com/py-pkgs/py-pkgs-cookiecutter).\n",
    "````\n",
    "\n",
    "When we render this Markdown text later on with `sphinx`, it will look like {numref}`03-documentation-1a-fig`. We'll talk about `sphinx` in **{numref}`03:Building-documentation`**, but many other tools are also able to natively render Markdown documents (e.g., Jupyter, VS Code, GitHub, etc.), which is why it's so widely used.\n",
    "\n",
    "```{figure} images/03-documentation-1.png\n",
    "---\n",
    "width: 100%\n",
    "name: 03-documentation-1a-fig\n",
    "alt: Rendered version of README.md.\n",
    "---\n",
    "Rendered version of README.md.\n",
    "```\n",
    "\n",
    "So, we now have a *`CHANGELOG.md`*, *`CONDUCT.md`*, *`CONTRIBUTING.md`*, *`LICENSE`*, and *`README.md`*. In the next section, we'll explain how to document your package's Python code using docstrings."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Writing-docstrings)=\n",
    "### Writing docstrings"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A docstring\\index{docstring} is a string, surrounded by triple-quotes, at the start of a module, class, or function in Python (preceding any code) that provides documentation on what the object does and how to use it. Docstrings automatically become the documented object's documentation, accessible to users via the `help()` function. Docstrings are a user's first port-of-call when they are trying to use your package, they really are a necessity when creating packages, even for yourself.\n",
    "\n",
    "General docstring convention in Python is described in [Python Enhancement Proposal (PEP) 257 — Docstring Conventions](https://www.python.org/dev/peps/pep-0257/), but there is flexibility in how you write your docstrings. A minimal docstring contains a single line describing what the object does, and that might be sufficient for a simple function or for when your code is in the early stages of development. However, for code you intend to share with others (including your future self) a more comprehensive docstring should be written. A typical docstring will include:\n",
    "\n",
    "1. A one-line summary that does not use variable names or the function name.\n",
    "2. An extended description.\n",
    "3. Parameter types and descriptions.\n",
    "4. Returned value types and descriptions.\n",
    "5. Example usage.\n",
    "6. Potentially more.\n",
    "\n",
    "There are different \"docstring styles\" used in Python to organize this information, such as [numpydoc style](https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard), [Google style](https://github.com/google/styleguide/blob/gh-pages/pyguide.md#38-comments-and-docstrings), and [sphinx style](https://sphinx-rtd-tutorial.readthedocs.io/en/latest/docstrings.html#the-sphinx-docstring-format). We'll be using the numpydoc style for our `pycounts` package because it is readable, commonly used, and supported by `sphinx`. In the numpydoc style:\n",
    "\n",
    "- Section headers are denoted as text underlined with dashes;\n",
    "\n",
    "    ```xml\n",
    "    Parameters\n",
    "    ----------\n",
    "    ```\n",
    "\n",
    "- Input arguments are denoted as:\n",
    "\n",
    "    ```xml\n",
    "    name : type\n",
    "        Description of parameter `name`.\n",
    "    ```\n",
    "\n",
    "- Output values use the same syntax above, but specifying the `name` is optional.\n",
    "\n",
    "We show a numpydoc style docstring for our `count_words()` function below:\n",
    "\n",
    "```python\n",
    "def count_words(input_file):\n",
    "    \"\"\"Count words in a text file.\n",
    "\n",
    "    Words are made lowercase and punctuation is removed \n",
    "    before counting.\n",
    "\n",
    "    Parameters\n",
    "    ----------\n",
    "    input_file : str\n",
    "        Path to text file.\n",
    "\n",
    "    Returns\n",
    "    -------\n",
    "    collections.Counter\n",
    "        dict-like object where keys are words and values are counts.\n",
    "\n",
    "    Examples\n",
    "    --------\n",
    "    >>> count_words(\"text.txt\")\n",
    "    \"\"\"\n",
    "    text = load_text(input_file)\n",
    "    text = clean_text(text)\n",
    "    words = text.split()\n",
    "    return Counter(words)\n",
    "```\n",
    "\n",
    "This docstrings can be accessed by users of our package by using the `help()` function in a Python interpreter:\n",
    "\n",
    "```\n",
    ">>> from pycounts.pycounts import count_words\n",
    ">>> help(count_words)\n",
    "```\n",
    "\n",
    "```md\n",
    "Help on function count_words in module pycounts.pycounts:\n",
    "\n",
    "count_words(input_file)\n",
    "    Count words in a text file.\n",
    "    \n",
    "    Words are made lowercase and punctuation is removed \n",
    "    before counting.\n",
    "\n",
    "    Parameters\n",
    "    ----------\n",
    "    ...\n",
    "```\n",
    "\n",
    "You can add information to your docstrings at your discretion — you won't always need all the sections above, and in some cases you may want to include additional sections from the numpydoc style [documentation](https://numpydoc.readthedocs.io/en/latest/format.html#docstring-standard). We've documented the remaining functions from our `pycounts` package as below. If you're following along with this tutorial, copy these docstrings into the functions in the `pycounts.pycounts` and `pycounts.plotting` modules:\n",
    "\n",
    "```python\n",
    "def plot_words(word_counts, n=10):\n",
    "    \"\"\"Plot a bar chart of word counts.\n",
    "    \n",
    "    Parameters\n",
    "    ----------\n",
    "    word_counts : collections.Counter\n",
    "        Counter object of word counts.\n",
    "    n : int, optional\n",
    "        Plot the top n words. By default, 10.\n",
    "\n",
    "    Returns\n",
    "    -------\n",
    "    matplotlib.container.BarContainer\n",
    "        Bar chart of word counts.\n",
    "\n",
    "    Examples\n",
    "    --------\n",
    "    >>> from pycounts.pycounts import count_words\n",
    "    >>> from pycounts.plotting import plot_words\n",
    "    >>> counts = count_words(\"text.txt\")\n",
    "    >>> plot_words(counts)\n",
    "    \"\"\"\n",
    "    top_n_words = word_counts.most_common(n)\n",
    "    word, count = zip(*top_n_words)\n",
    "    fig = plt.bar(range(n), count)\n",
    "    plt.xticks(range(n), labels=word, rotation=45)\n",
    "    plt.xlabel(\"Word\")\n",
    "    plt.ylabel(\"Count\")\n",
    "    return fig\n",
    "```\n",
    "\n",
    "\\newpage\n",
    "\n",
    "```python\n",
    "def load_text(input_file):\n",
    "    \"\"\"Load text from a text file and return as a string.\n",
    "\n",
    "    Parameters\n",
    "    ----------\n",
    "    input_file : str\n",
    "        Path to text file.\n",
    "\n",
    "    Returns\n",
    "    -------\n",
    "    str\n",
    "        Text file contents.\n",
    "\n",
    "    Examples\n",
    "    --------\n",
    "    >>> load_text(\"text.txt\")\n",
    "    \"\"\"\n",
    "    with open(input_file, \"r\") as file:\n",
    "        text = file.read()\n",
    "    return text\n",
    "```\n",
    "\n",
    "```python\n",
    "def clean_text(text):\n",
    "    \"\"\"Lowercase and remove punctuation from a string.\n",
    "\n",
    "    Parameters\n",
    "    ----------\n",
    "    text : str\n",
    "        Text to clean.\n",
    "\n",
    "    Returns\n",
    "    -------\n",
    "    str\n",
    "        Cleaned text.\n",
    "\n",
    "    Examples\n",
    "    --------\n",
    "    >>> clean_text(\"Early optimization is the root of all evil!\")\n",
    "    'early optimization is the root of all evil'\n",
    "    \"\"\"\n",
    "    text = text.lower()\n",
    "    for p in punctuation:\n",
    "        text = text.replace(p, \"\")\n",
    "    return text\n",
    "```\n",
    "\n",
    "For the users of our package it would be helpful to compile all of our functions and docstrings into a easy-to-navigate document, so they can access this documentation without having to `import` them and run `help()`, or search through our source code. Such a document is referred to as an application programming interface\\index{application programming interface} (API) reference. We could create one by manually copying and pasting all of our function names and docstrings into a plain-text file, but that would be inefficient. Instead, we'll show how to use `sphinx` in **{numref}`03:Building-documentation`** to automatically parse our source code, extract our functions and docstrings, and create an API reference for us."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Creating-usage-examples)=\n",
    "### Creating usage examples"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Creating examples of how to use your package can be invaluable to new and existing users alike. Unlike the brief and basic \"Usage\" heading we wrote in our README in **{numref}`03:Writing-documentation`**, these examples are more like tutorials, including a mix of text and code that demonstrates the functionality and common workflows of your package step-by-step.\n",
    "\n",
    "You could write examples from scratch using a plain-text format like Markdown\\index{Markdown}, but this can be inefficient and prone to errors. If you change the way a function works, or what it outputs, you would have to re-write your example. Instead, in this section we'll show how to use Jupyter Notebooks {cite:p}`jupyter2016` as a more efficient, interactive, and reproducible way to create usage examples for your users. If you don't want to create usage examples for your package, or aren't interested in learning how to use Jupyter Notebooks to do so, you can skip to **{numref}`03:Building-documentation`**.\n",
    "\n",
    "Jupyter\\index{Jupyter} Notebooks\\index{Jupyter!notebooks} are interactive documents with an *.ipynb* extension that can contain code, equations, text, and visualizations. They are effective for demonstrating examples because they directly import and use code from your package; this ensures you don't make mistakes when writing out your examples, and it allows users to download, execute, and interact with the notebooks themselves (as opposed to just reading text). To create a usage example for our `pycounts` package using a Jupyter Notebook, we first need to add `jupyter` as a development dependency\\index{dependency!development dependency}:\n",
    "\n",
    "```{attention}\n",
    "If using a `conda` virtual environment, make sure that environment is active by running `conda activate pycounts`, before using or working on your package.\n",
    "```\n",
    "\n",
    "```\n",
    "$ poetry add --group dev jupyter\n",
    "```\n",
    "\n",
    "Our `py-pkgs-cookiecutter` template already created a Jupyter Notebook example document for us at *`docs/example.ipynb`*. To edit that document, we first open the Jupyter Notebook application using the following command from the root package directory:\n",
    "\n",
    "```\n",
    "$ jupyter notebook\n",
    "```\n",
    "\n",
    "```{note}\n",
    "If you're developing your Python package in an IDE\\index{integrated development environment} that natively supports Jupyter Notebooks, such as Visual Studio Code\\index{Visual Studio Code} or JupyterLab, you can simply open *`docs/example.ipynb`* to edit it, without needing to run the `jupyter notebook` command above.\n",
    "```\n",
    "\n",
    "In the interface, navigate to and open *`docs/example.ipynb`*. As explained in the Jupyter Notebook [documentation](https://jupyter-notebook.readthedocs.io/en/stable/), notebooks are comprised of \"cells\", which can contain Python code or Markdown text. Our notebook currently looks like {numref}`03-jupyter-example-1-fig`.\n",
    "\n",
    "```{figure} images/03-jupyter-example-1.png\n",
    "---\n",
    "width: 100%\n",
    "name: 03-jupyter-example-1-fig\n",
    "alt: A simple Jupyter Notebook using code from pycounts.\n",
    "---\n",
    "A simple Jupyter Notebook using code from pycounts.\n",
    "```\n",
    "\n",
    "As an example, we'll update our notebook with the collection of Markdown and code cells shown in {numref}`03-jupyter-example-2-fig` and {numref}`03-jupyter-example-3-fig`.\n",
    "\n",
    "```{figure} images/03-jupyter-example-2.png\n",
    "---\n",
    "width: 100%\n",
    "name: 03-jupyter-example-2-fig\n",
    "alt: First half of Jupyter Notebook demonstrating an example workflow using the pycounts package.\n",
    "---\n",
    "First half of Jupyter Notebook demonstrating an example workflow using the pycounts package.\n",
    "```\n",
    "\n",
    "\\newpage\n",
    "\n",
    "```{figure} images/03-jupyter-example-3.png\n",
    "---\n",
    "width: 100%\n",
    "name: 03-jupyter-example-3-fig\n",
    "alt: Second half of Jupyter Notebook demonstrating an example workflow using the pycounts package.\n",
    "---\n",
    "Second half of Jupyter Notebook demonstrating an example workflow using the pycounts package.\n",
    "```\n",
    "\n",
    "Our Jupyter Notebook now contains an interactive tutorial demonstrating the basic usage of our package. What's important to note is that the code and outputs are generated using our package itself, they have not been written manually. Our users could now also download our example notebook and interact and execute it themselves. But in the next section, we'll show how to use `sphinx` to automatically execute notebooks and include their content (including the outputs of code cells) into a compiled collection of all our package's documentation that users can easily read and navigate through without even having to start the Jupyter application!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Building-documentation)=\n",
    "### Building documentation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We've now written all the individual pieces of documentation\\index{documentation} needed to support our `pycounts` package. But all this documentation is spread over the directory structure of our package making it difficult to share and search through.\n",
    "\n",
    "This is where the documentation generator `sphinx`\\index{sphinx} comes in. `sphinx` is a tool used to compile and render collections of plain-text source files into user-friendly output formats, such as HTML or PDF. `sphinx` also has a rich ecosystem of extensions that can be used to help automatically generate content — we'll be using some of these extensions in this section to automatically create an API reference sheet from our docstrings, and to execute and render our Jupyter Notebook example into our documentation.\n",
    "\n",
    "To first give you an idea of what we're going to build\\index{documentation!build documentation}, {numref}`03-documentation-1b-fig` shows the homepage of our package's documentation compiled by `sphinx` into HTML.\n",
    "\n",
    "```{figure} images/03-documentation-1.png\n",
    "---\n",
    "width: 100%\n",
    "name: 03-documentation-1b-fig\n",
    "alt: The documentation homepage generated by sphinx.\n",
    "---\n",
    "The documentation homepage generated by sphinx.\n",
    "```\n",
    "\n",
    "The source and configuration files to build documentation like this using `sphinx` typically live in the *`docs/`* directory in a package's root. The `py-pkgs-cookiecutter`\\index{py-pkgs-cookiecutter} automatically created this directory and the necessary files for us. We'll discuss what each of these files are used for below.\n",
    "\n",
    "```{code-block} md\n",
    "---\n",
    "emphasize-lines: 6-15\n",
    "---\n",
    "pycounts\n",
    "├── .readthedocs.yml\n",
    "├── CHANGELOG.md\n",
    "├── CONDUCT.md\n",
    "├── CONTRIBUTING.md\n",
    "├── docs\n",
    "│   ├── changelog.md\n",
    "│   ├── conduct.md\n",
    "│   ├── conf.py\n",
    "│   ├── contributing.md\n",
    "│   ├── example.ipynb\n",
    "│   ├── index.md\n",
    "│   ├── make.bat\n",
    "│   ├── Makefile\n",
    "│   └── requirements.txt\n",
    "├── LICENSE\n",
    "├── poetry.lock\n",
    "├── pyproject.toml\n",
    "├── README.md\n",
    "├── src\n",
    "│   └── ...\n",
    "└── tests\n",
    "    └── ...\n",
    "```\n",
    "\n",
    "The *`docs/`* directory includes:\n",
    "\n",
    "- *`Makefile`*/*`make.bat`*: files that contain commands needed to build our documentation with `sphinx` and do not need to be modified. [Make](https://www.gnu.org/software/make/)\\index{Make} is a tool used to run commands to efficiently read, process, and write files. A Makefile\\index{Make!Makefile} defines the tasks for Make to execute. If you're interested in learning more about Make, we recommend the [Learn Makefiles](https://makefiletutorial.com) tutorial. But for building documentation with `sphinx`, all you need to know is that having these Makefiles allows us to build documentation with the simple command `make html`, which we'll do later in this section.\n",
    "\n",
    "\\newpage\n",
    "\n",
    "- *`requirements.txt`*: contains a list of documentation-specific dependencies required to host our documentation online on [Read the Docs](https://readthedocs.org/), which we'll discuss in **{numref}`03:Hosting-documentation-online`**.\n",
    "- *`conf.py`* is a configuration file controlling how `sphinx` builds your documentation. You can read more about *`conf.py`* in the `sphinx` [documentation](https://www.sphinx-doc.org/en/master/usage/configuration.html) and we'll touch on it again shortly, but, for now, it has been pre-populated by the `py-pkgs-cookiecutter` template and does not need to be modified.\n",
    "- The remaining files in the *`docs/`* directory form the content of our generated documentation, as we'll discuss in the remainder of this section.\n",
    "\n",
    "The *`index.md`* file will form the landing page of our documentation (the one we saw earlier in {numref}`03-documentation-1b-fig`). Think of it as the homepage of a website. For your landing page, you'd typically want some high-level information about your package, and then links to the rest of the documentation you want to expose to a user. If you open *`index.md`* in an editor of your choice, that's exactly the content we are including, with a particular kind of syntax, which we explain below.\n",
    "\n",
    "````xml\n",
    "```{include} ../README.md\n",
    "```\n",
    "\n",
    "```{toctree}\n",
    ":maxdepth: 1\n",
    ":hidden:\n",
    "\n",
    "example.ipynb\n",
    "changelog.md\n",
    "contributing.md\n",
    "conduct.md\n",
    "autoapi/index\n",
    "```\n",
    "````\n",
    "\n",
    "The syntax we're using in this file is known as [Markedly Structured Text\\index{Markedly Structured Text} (MyST)](https://myst-parser.readthedocs.io/en/latest/syntax/syntax.html). MyST is based on Markdown but with additional syntax options compatible for use with `sphinx`. The `{include}` syntax specifies that when this page is rendered with `sphinx`, we want it to include the content of the *`README.md`* from our package's root directory (think of it as a copy-paste operation).\n",
    "\n",
    "The `{toctree}` syntax defines what documents will be listed in the table of contents (ToC) on the left-hand side of our rendered documentation, as shown in {numref}`03-documentation-1b-fig`. The argument `:maxdepth: 1` indicates how many heading levels the ToC should include, and `:hidden:` specifies that the ToC should only appear in the side bar and not in the welcome page itself. The ToC then lists the documents to include in our rendered documentation.\n",
    "\n",
    "\"example.ipynb\" is the Jupyter Notebook we wrote in section **{numref}`03:Creating-usage-examples`**. `sphinx` doesn't support relative links in a ToC, so to include the documents *`CHANGELOG.md`*, *`CONTRIBUTING.md`*, *`CONDUCT.md`* from our package's root, we create \"stub files\" called *`changelog.md`*, *`contributing.md`*, and *`conduct.md`*, which link to these documents using the `{include}` syntax we saw earlier. For example, *`changelog.md`* contains the following text:\n",
    "\n",
    "````xml\n",
    "```{include} ../CHANGELOG.md\n",
    "```\n",
    "````\n",
    "\n",
    "The final document in the ToC, \"autoapi/index\" is an API reference sheet that will be generated automatically for us, from our package structure and docstrings, when we build our documentation with `sphinx`.\n",
    "\n",
    "Before we can go ahead and build our documentation with `sphinx`, it relies on a few `sphinx` extensions that need to be installed and configured:\n",
    "\n",
    "- [myst-nb](https://myst-nb.readthedocs.io/en/latest/): extension that will enable `sphinx` to parse our Markdown, MyST, and Jupyter Notebook files (`sphinx` only supports reStructuredTex, *.rst* files, by default).\n",
    "- [sphinx-rtd-theme](https://sphinx-rtd-theme.readthedocs.io/en/stable/): a custom theme for styling the way our documentation will look. It looks much better than the default theme.\n",
    "- [sphinx-autoapi](https://sphinx-autoapi.readthedocs.io/en/latest/): extension that will parse our source code and docstrings to create an API reference sheet.\n",
    "- [sphinx.ext.napoleon](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/): extension that enables `sphinx` to parse numpydoc style docstrings.\n",
    "- [sphinx.ext.viewcode](https://www.sphinx-doc.org/en/master/usage/extensions/viewcode.html): extension that adds a helpful link to the source code of each object in the API reference sheet.\n",
    "\n",
    "These extensions are not necessary to create documentation with `sphinx`, but they are all commonly used in Python packaging documentation and significantly improve the look and user-experience of the generated documentation. Extensions without the `sphinx.ext` prefix need to be installed. We can install them as development dependencies in a `poetry`-managed project with the following command:\n",
    "\n",
    "```{attention}\n",
    "If using a `conda` virtual environment, make sure that environment is active by running `conda activate pycounts`, before using or working on your package.\n",
    "```\n",
    "\n",
    "```\n",
    "$ poetry add --group dev myst-nb --python \"^3.9\"\n",
    "$ poetry add --group dev sphinx-autoapi sphinx-rtd-theme\n",
    "```\n",
    "\n",
    "```{attention}\n",
    "Adding `myst-nb` is a great example of why upper caps on dependency versions can be a pain, as we discussed in **{numref}`03:Dependency-version-constraints`**. At the time of writing, one of the dependencies of `myst-nb`, `mdit-py-plugins`, has an upper cap of <4.0 on the Python version it requires, so it's not compatible with our package and its other dependencies which all support Python >=3.9. Thus, unless `mdit-py-plugins` removes this upper cap, the easiest way for us to add `myst-nb` is to tell `poetry` to only install it for Python versions ^3.9 (i.e., >=3.9 and <4.0), by using the argument `--python \"^3.9\"`.\n",
    "```\n",
    "\n",
    "Once installed, any extensions you want to use need to be added to a list called `extensions` in the *`conf.py`* configuration file and configured. Configuration options for each extension (if they exist) can be viewed in their respective documentation, but the `py-pkgs-cookeicutter` has already taken care of everything for us, by defining the following variables within *`conf.py`*:\n",
    "\n",
    "```python\n",
    "extensions = [\n",
    "    \"myst_nb\",\n",
    "    \"autoapi.extension\",\n",
    "    \"sphinx.ext.napoleon\",\n",
    "    \"sphinx.ext.viewcode\"\n",
    "]\n",
    "autoapi_dirs = [\"../src\"]  # location to parse for API reference\n",
    "html_theme = \"sphinx_rtd_theme\"\n",
    "```\n",
    "\n",
    "With our documentation structure set up, and our extensions configured, we can now navigate to the *`docs/`* directory and build our documentation with `sphinx` using the following commands:\n",
    "\n",
    "```\n",
    "$ cd docs\n",
    "$ make html\n",
    "```\n",
    "\n",
    "```md\n",
    "Running Sphinx\n",
    "...\n",
    "build succeeded.\n",
    "The HTML pages are in _build/html.\n",
    "```\n",
    "\n",
    "If we look inside our *`docs/`* directory we see a new directory *`_build/html`*, which contains our built documentation as HTML files. If you open *`_build/html/index.html`*, you should see the page shown earlier in {numref}`03-documentation-1b-fig`.\n",
    "\n",
    "```{note}\n",
    "If you make significant changes to your documentation, it can be a good idea to delete the *`_build/`* folder before building it again. You can do this easily by adding the `clean` option into the `make html` command: `make clean html`.\n",
    "```\n",
    "\n",
    "The `sphinx-autoapi` extension extracted the docstrings we wrote for our package's functions in **{numref}`03:Writing-docstrings`** and rendered them into our documentation. You can find the generated API reference sheet by clicking \"API Reference\" in the table of contents. For example, {numref}`03-documentation-2-fig` shows the functions and docstrings in the `pycounts.plotting` module. The `sphinx.ext.viewcode` extension added the \"source\" button next to each function in our API reference sheet, which links readers directly to the source code of the function (if they want to view it).\n",
    "\n",
    "\\newpage\n",
    "\n",
    "```{figure} images/03-documentation-2.png\n",
    "---\n",
    "width: 100%\n",
    "name: 03-documentation-2-fig\n",
    "alt: Documentation for the pycounts plotting module.\n",
    "---\n",
    "Documentation for the pycounts plotting module.\n",
    "```\n",
    "\n",
    "Finally, if we navigate to the \"Example usage\" page, {numref}`03-documentation-3-fig` shows the Jupyter Notebook\\index{Jupyter!notebook} we wrote in **{numref}`03:Creating-usage-examples`** rendered into our documentation, including the Markdown text, code input, and executed output. This was made possible using the `myst-nb` extension.\n",
    "\n",
    "\\newpage\n",
    "\n",
    "```{figure} images/03-documentation-3.png\n",
    "---\n",
    "width: 100%\n",
    "name: 03-documentation-3-fig\n",
    "alt: Jupyter Notebook example rendered into pycounts's documentation.\n",
    "---\n",
    "Jupyter Notebook example rendered into pycounts's documentation.\n",
    "```\n",
    "\n",
    "Ultimately, you can efficiently make beautiful and many-featured documentation with `sphinx` and its ecosystem of extensions. You can now use this documentation yourself or potentially share it with others, but it really shines when you host it on the web using a free service like [Read the Docs](https://readthedocs.org/), as we'll do in the next section. For those using version control\\index{version control}, now is a good time to move back to our package's root directory and commit our work using the following commands:\n",
    "\n",
    "\\newpage\n",
    "\n",
    "```\n",
    "$ cd ..\n",
    "$ git add README.md docs/example.ipynb\n",
    "$ git commit -m \"docs: updated readme and example\"\n",
    "$ git add src/pycounts/pycounts.py src/pycounts/plotting.py\n",
    "$ git commit -m \"docs: created docstrings for package functions\"\n",
    "$ git add pyproject.toml poetry.lock\n",
    "$ git commit -m \"build: added dev dependencies for docs\"\n",
    "$ git push\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Hosting-documentation-online)=\n",
    "### Hosting documentation online"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you intend to share your package with others, it will be useful to make your documentation accessible online\\index{documentation!host documentation}. It's common to host Python package documentation on the free online hosting service [Read the Docs](https://readthedocs.org/)\\index{Read the Docs}. Read the Docs works by connecting to an online repository hosting your package documentation, such as a GitHub repository. When you push changes to your repository, Read the Docs automatically builds a fresh copy of your documentation (i.e., runs `make html`) and hosts it at the URL `https://<pkgname>.readthedocs.io/` (you can also configure Read the Docs to use a custom domain name). This means that any changes you make to your documentation source files (and push to your linked remote repository) are immediately deployed to your users. If you need your documentation to be private (e.g., only available to employees of a company), Read the Docs offers a paid \"Business plan\" with this functionality.\n",
    "\n",
    "```{tip}\n",
    "[GitHub Pages](https://pages.github.com)\\index{GitHub Pages} is another popular service used for hosting documentation from a repository. However, it doesn't natively support automatic building of your documentation when you push changes to the source files, which is why we prefer to use Read the Docs here. If you did want to host your docs on GitHub Pages, we recommend using the [ghp-import](https://github.com/c-w/ghp-import) package, or setting up an automated GitHub Actions workflow using the [peaceiris/actions-gh-pages](https://github.com/peaceiris/actions-gh-pages) action (we'll learn more about GitHub Actions in **Chapter 8: {ref}`08:Continuous-integration-and-deployment`**).\n",
    "```\n",
    "\n",
    "\\newpage\n",
    "\n",
    "The [Read the Docs](https://readthedocs.org) documentation will provide the most up-to-date steps required to host your documentation online. For our `pycounts` package, this involved the following steps:\n",
    "\n",
    "1. Visit <https://readthedocs.org/> and click on \"Sign up\".\n",
    "2. Select \"Sign up with GitHub\".\n",
    "3. Click \"Import a Project\".\n",
    "4. Click \"Import Manually\".\n",
    "5. Fill in the project details by:\n",
    "    1. Providing your package name (e.g., `pycounts`).\n",
    "    2. The URL to your package's GitHub repository (e.g., `https://github.com/TomasBeuzen/pycounts`).\n",
    "    3. Specify the default branch as `main`.\n",
    "6. Click \"Next\" and then \"Build version\".\n",
    "\n",
    "After following the steps above, your documentation should be successfully built by [Read the Docs](https://readthedocs.org/), and you should be able to access it via the \"View Docs\" button on the build page. For example, the documentation for `pycounts` is now available at <https://pycounts.readthedocs.io/en/latest/>. This documentation will be automatically re-built by Read the Docs each time you push changes to your GitHub repository.\n",
    "\n",
    "```{attention}\n",
    "The *`.readthedocs.yml`* file that `py-pkgs-cookiecutter` created for us in the root directory of our Python package contains the configuration settings necessary for Read the Docs to properly build our documentation. It specifies what version of Python to use and tells Read the Docs that our documentation requires the extra packages specified in *`pycounts/docs/requirements.txt`* to be generated correctly.\n",
    "```\n",
    "\n",
    "\\newpage"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Tagging-a-package-release-with-version-control)=\n",
    "## Tagging a package release with version control"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We have now created all the source files that make up version 0.1.0 of our `pycounts` package, including Python code, documentation, and tests — well done! In the next section, we'll turn all these source files into a distribution package that can be easily shared and installed by others. But for those using version control\\index{version control}, it's helpful at this point to tag\\index{version control!tag} a release\\index{version control!release} of your package's repository. If you're not using version control, you can skip to **{numref}`03:Building-and-distributing-your-package`**.\n",
    "\n",
    "Tagging a release means that we permanently \"tag\" a specific point in our repository's history, and then create a downloadable \"release\" of all the files in our repository in the state they were in when the tag was made. It's common to tag a release for each new version of your package, as we'll discuss more in **Chapter 7: {ref}`07:Releasing-and-versioning`**.\n",
    "\n",
    "Tagging a release is a two-step process involving both Git and GitHub:\n",
    "\n",
    "1. Create a tag marking a specific point in a repository's history using the command `git tag`\\index{Git}.\n",
    "2. On GitHub\\index{GitHub}, create a release of all the files in your repository (usually in the form of a zipped archive like *.zip* or *.tar.gz*) based on your tag. Others can then download this release if they wish to view or use your package's source files as they existed at the time the tag was created.\n",
    "\n",
    "We'll demonstrate this process by tagging a release of v0.1.0 of our `pycounts` package (it's common to prefix a tag with \"v\" for \"version\"). First, we need to create a tag identifying the state of our repository at v0.1.0 and then push the tag to GitHub using the following `git` commands at the command line:\n",
    "\n",
    "```\n",
    "$ git tag v0.1.0\n",
    "$ git push --tags\n",
    "```\n",
    "\n",
    "Now if you go to the `pycounts` repository on GitHub and navigate to the \"Releases\" tab, you should see a tag like that shown in {numref}`03-tag-fig`.\n",
    "\n",
    "\\newpage\n",
    "\n",
    "```{figure} images/03-tag.png\n",
    "---\n",
    "width: 100%\n",
    "name: 03-tag-fig\n",
    "alt: Tag of v0.1.0 of pycounts on GitHub.\n",
    "---\n",
    "Tag of v0.1.0 of pycounts on GitHub.\n",
    "```\n",
    "\n",
    "To create a release from this tag, click \"Draft a new release\". You can then identify the tag from which to create the release and optionally add some additional details about the release as shown in {numref}`03-release-1-fig`.\n",
    "\n",
    "\\newpage\n",
    "\n",
    "```{figure} images/03-release-1.png\n",
    "---\n",
    "width: 100%\n",
    "name: 03-release-1-fig\n",
    "alt: Making a release of v0.1.0 of pycounts on GitHub.\n",
    "---\n",
    "Making a release of v0.1.0 of pycounts on GitHub.\n",
    "```\n",
    "\n",
    "After clicking \"Publish release\", GitHub will automatically create a release from your tag, including compressed archives of your code in *.zip* and *.tar.gz* format, as shown in {numref}`03-release-2-fig`.\n",
    "\n",
    "\\newpage\n",
    "\n",
    "```{figure} images/03-release-2.png\n",
    "---\n",
    "width: 100%\n",
    "name: 03-release-2-fig\n",
    "alt: Release of v0.1.0 of pycounts on GitHub.\n",
    "---\n",
    "Release of v0.1.0 of pycounts on GitHub.\n",
    "```\n",
    "\n",
    "We'll talk more about making new versions and releases of your package as you update it (e.g., modify code, add features, fix bugs, etc.) in **Chapter 7: {ref}`07:Releasing-and-versioning`**.\n",
    "\n",
    "```{tip}\n",
    "People with access to your GitHub repository can actually `pip install` your package directly from the repository using your tags. We talk more about that in **{numref}`04:Package-repositories`**.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Building-and-distributing-your-package)=\n",
    "## Building and distributing your package"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Building-your-package)=\n",
    "### Building your package"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Right now, our package is a collection of files and folders that is difficult to share with others. The solution to this problem is to create a \"distribution package\\index{distribution}\". A distribution package is a single archive file containing all the files and information necessary to install a package using a tool like `pip`\\index{pip}. Distribution packages are often called \"distributions\" for short and they are how packages are shared in Python and installed by users, typically with the command `pip install <some-package>`.\n",
    "\n",
    "The main types of distributions in Python are source distributions (known as \"sdists\") and wheels. sdists\\index{distribution!sdists} are a compressed archive of all the source files, metadata, and instructions needed to construct an installable version of your package. To install from an sdist, a user needs to download the sdist, extract its contents, and then use the build instructions to build and finally install the package on their computer.\n",
    "\n",
    "In contrast, wheels\\index{distribution!wheel} are pre-built versions of a package. They are built on the developer's machine before sharing with users. They are the preferred distribution format because a user only needs to download the wheel and move it to the location on their computer where Python searches for packages; no build step is required.\n",
    "\n",
    "`pip install` can handle installation from either an sdist or a wheel, and we'll discuss these topics in much more detail in **{numref}`04:Package-distribution-and-installation`**. What you need to know now is that when distributing a package it's common to create both sdist and wheel distributions. We can easily create an sdist and wheel of a package with `poetry`\\index{poetry} using the command `poetry build`. Let's do that now for our `pycounts` package by running the following command from our root package directory:\n",
    "\n",
    "```\n",
    "$ poetry build\n",
    "```\n",
    "\n",
    "```md\n",
    "Building pycounts (0.1.0)\n",
    "  - Building sdist\n",
    "  - Built pycounts-0.1.0.tar.gz\n",
    "  - Building wheel\n",
    "  - Built pycounts-0.1.0-py3-none-any.whl\n",
    "```\n",
    "\n",
    "After running this command, you'll notice a new directory in your package called *`dist/`*:\n",
    "\n",
    "```md\n",
    "pycounts\n",
    "├── .readthedocs.yml\n",
    "├── CHANGELOG.md\n",
    "├── CONDUCT.md\n",
    "├── CONTRIBUTING.md\n",
    "├── dist\n",
    "│   ├── pycounts-0.1.0-py3-none-any.whl  <- wheel\n",
    "│   └── pycounts-0.1.0.tar.gz            <- sdist\n",
    "├── docs\n",
    "│   └── ...\n",
    "├── LICENSE\n",
    "├── poetry.lock\n",
    "├── pyproject.toml\n",
    "├── README.md\n",
    "├── src\n",
    "│   └── ...\n",
    "└── tests\n",
    "    └── ...\n",
    "```\n",
    "\n",
    "Those two new files are the sdist and wheel for our `pycounts` package. A user could now easily install our package\\index{installable Python package} if they had one of these distributions by using `pip install`. For example, to install the wheel (the preferred distribution type), you could enter the following in a terminal:\n",
    "\n",
    "```\n",
    "$ cd dist/\n",
    "$ pip install pycounts-0.1.0-py3-none-any.whl\n",
    "```\n",
    "\n",
    "```md\n",
    "Processing ./pycounts-0.1.0-py3-none-any.whl\n",
    "...\n",
    "Successfully installed pycounts-0.1.0\n",
    "```\n",
    "\n",
    "To install using the sdist, you would have to unpack the sdist archive before running `pip install`. The procedure for this varies depending on your specific operating system. For example, on Mac OS, the command line tool `tar` with argument `x` (extract the input file), `z` (gunzip the input file), `f` (apply operations to the provided input file) can be used to unpack the sdist:\n",
    "\n",
    "```\n",
    "$ tar xzf pycounts-0.1.0.tar.gz\n",
    "$ pip install pycounts-0.1.0/\n",
    "```\n",
    "\n",
    "\\newpage\n",
    "\n",
    "```md\n",
    "Processing ./pycounts-0.1.0-py3-none-any.whl\n",
    "  Installing build dependencies ... done\n",
    "    Getting requirements to build wheel ... done\n",
    "    Preparing wheel metadata ... done\n",
    "...\n",
    "Successfully built pycounts\n",
    "Successfully installed pycounts-0.1.0\n",
    "```\n",
    "\n",
    "Note in the output above how installing from an sdist requires a build step prior to installation. The sdist is first built into a wheel, which is then installed. For those interested, we discuss the nuances of building and installing packages from sdists and wheels in **{numref}`04:Package-distribution-and-installation`**.\n",
    "\n",
    "Creating a distribution for our package is most useful if we make it available on an online repository like the Python Package Index (PyPI\\index{PyPI}), the official online software repository for Python. This would allow users to simply run `pip install pycounts` to install our package, without needing the sdist or wheel files locally, and we'll do this in the next section. But even if you don't intend to share your package, it can still be useful to build and install distributions for two reasons:\n",
    "\n",
    "1. A distribution is a self-contained copy of your package's source files that's easy to move around and store on your computer. It makes it easy to retain distributions for different versions of your package, so that you can re-use or share them if you ever need to.\n",
    "2. Recall that `poetry` installs package in \"editable mode\", such that a link to the package's location is installed, rather than an independent distribution of the package itself. This is useful for *development purposes*, because it means that any changes to the source code will be immediately reflected when you next `import` the package, without the need to `poetry install` again. However, for *users* of your package (including yourself using your package in other projects), it is often better to install a \"non-editable\" version of the package (the default behavior when you `pip install` an sdist or wheel) because a non-editable installation will remain stable and immune to any changes made to the source files on your computer."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Publishing-to-TestPyPI)=\n",
    "### Publishing to TestPyPI"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "At this point, we have distributions of `pycounts` that we want to share with the world by publishing\\index{publish} to [PyPI](https://pypi.org/)\\index{PyPI}. However, it is good practice to do a \"dry run\" and check that everything works as expected by submitting to [TestPyPi](https://test.pypi.org/) first. `poetry`\\index{poetry} has a `publish` command, which we can use to do this, however the default behavior is to publish to PyPI. So we need to add TestPyPI\\index{TestPyPI} to the list of repositories\\index{software repository} `poetry` knows about using the following command:\n",
    "\n",
    "```\n",
    "$ poetry config repositories.test-pypi https://test.pypi.org/legacy/\n",
    "```\n",
    "\n",
    "To publish to TestPyPI we can use `poetry publish`. To do this we will be using the authentication token which we signed up for in **{numref}`02:Register-for-a-PyPI-account`**.\n",
    "You will need to tell `poetry`\\index{poetry} that you will be authenticating using a token via `-u __token__` and pass it the authentication token via `-p yourlongtestpypitokengoeshere`:\n",
    "\n",
    "```\n",
    "$ poetry publish -r test-pypi -u __token__ -p yourlongtestpypitokengoeshere...\n",
    "```\n",
    "\n",
    "```md\n",
    "Publishing pycounts (0.1.0) to test-pypi\n",
    " - Uploading pycounts-0.1.0-py3-none-any.whl 100%\n",
    " - Uploading pycounts-0.1.0.tar.gz 100%\n",
    "```\n",
    "\n",
    "Now we should be able to visit our package on TestPyPI. The URL for our `pycounts` package is: <https://test.pypi.org/project/pycounts/>. We can try installing our package using `pip` from the command line with the following command:\n",
    "\n",
    "```\n",
    "$ pip install --index-url https://test.pypi.org/simple/ \\\n",
    "  --extra-index-url https://pypi.org/simple \\\n",
    "  pycounts\n",
    "```\n",
    "\n",
    "By default `pip install` will search PyPI for the named package. However, we want to search TestPyPI because that is where we uploaded our package. The argument `--index-url` points `pip` to the TestPyPI index. However, it's important to note that not all developers upload their packages to TestPyPI; some only upload them directly to PyPI. If your package depends on packages that are not on TestPyPI you can tell `pip` to try and look for them on PyPI instead. To do that, you can use the argument `--extra-index-url` as we do in the command above."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(03:Publishing-to-PyPI)=\n",
    "### Publishing to PyPI"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you were able to upload your package to TestPyPI\\index{TestPyPI} and install it without error, you're ready to publish your package to PyPI\\index{PyPI}. You can publish\\index{publish} to PyPI using the `poetry publish` by supplying your token (for PyPI this time, not TestPyPI):\n",
    "\n",
    "```\n",
    "$ poetry publish -u __token__ -p yourlongpypitokengoeshere...\n",
    "```\n",
    "\n",
    "```{note}\n",
    "We omitted the -r flag this time because the default repository for `poetry publish` is PyPI.\n",
    "```\n",
    "\n",
    "Your package will then be available on PyPI (e.g., <https://pypi.org/project/pycounts/>) and can be installed by anyone using `pip`:\n",
    "\n",
    "```\n",
    "$ pip install pycounts\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Summary and next steps"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This chapter provided a practical overview of the key steps required to generate a fully-featured Python package. In the following chapters, we'll explore each of these steps in more detail and continue to add features to our `pycounts` package. Two key workflows we have yet to discuss are:\n",
    "\n",
    "1. Releasing new versions of your package as you update it. We'll discuss this in **Chapter 7: {ref}`07:Releasing-and-versioning`**.\n",
    "2. Setting up continuous integration and continuous deployment (CI/CD) — that is, automated pipelines for running tests, building documentation, and deploying your package. We'll discuss CI/CD in **Chapter 8: {ref}`08:Continuous-integration-and-deployment`**.\n",
    "\n",
    "Before moving onto the next chapter, let's summarize a reference list of all the steps we took to develop a Python package in this chapter:\n",
    "\n",
    "1. Create package structure using `cookiecutter` (**{numref}`03:Creating-a-package-structure`**).\n",
    "\n",
    "    ```\n",
    "    $ cookiecutter \\\n",
    "      https://github.com/py-pkgs/py-pkgs-cookiecutter.git\n",
    "    ```\n",
    "    \n",
    "2. (Optional) Put your package under version control (**{numref}`03:Put-your-package-under-version-control`**).\n",
    "3. (Optional) Create and activate a virtual environment using `conda` (**{numref}`03:Create-a-virtual-environment`**).\n",
    "\n",
    "    ```\n",
    "    $ conda create --name <your-env-name> python=3.9 -y\n",
    "    $ conda activate <your-env-name>\n",
    "    ```\n",
    "    \n",
    "4. Add Python code to module(s) in the *`src/`* directory (**{numref}`03:Packaging-your-code`**), adding dependencies as needed (**{numref}`03:Adding-dependencies-to-your-package`**).\n",
    "\n",
    "    ```\n",
    "    $ poetry add <dependency>\n",
    "    ```\n",
    "    \n",
    "5. Install and try out your package in a Python interpreter (**{numref}`03:Installing-your-package`**).\n",
    "\n",
    "    ```\n",
    "    $ poetry install\n",
    "    ```\n",
    "    \n",
    "6. (Optional) Write tests for your package in module(s) prefixed with *`test_`* in the *`tests/`* directory. Add `pytest` as a development dependency to run your tests (**{numref}`03:Running-tests`**). Add `pytest-cov` as a development dependency to calculate the coverage of your tests (**{numref}`03:Code-coverage`**).\n",
    "\n",
    "    ```\n",
    "    $ poetry add --group dev pytest pytest-cov\n",
    "    $ pytest tests/ --cov=<pkg-name>\n",
    "    ```\n",
    "    \n",
    "7. (Optional) Create documentation source files for your package (**{numref}`03:Package-documentation`**). Use `sphinx` to compile and generate an HTML render of your documentation, adding the required development dependencies (**{numref}`03:Building-documentation`**).\n",
    "    \n",
    "    ```\n",
    "    $ poetry add --group dev myst-nb sphinx-autoapi sphinx-rtd-theme\n",
    "    $ cd docs\n",
    "    $ make html\n",
    "    $ cd ..\n",
    "    ```\n",
    "    \n",
    "8. (Optional) Host documentation online with [Read the Docs](https://readthedocs.org/) (**{numref}`03:Hosting-documentation-online`**).\n",
    "9. (Optional) Tag a release of your package using Git and GitHub, or equivalent version control tools (**{numref}`03:Tagging-a-package-release-with-version-control`**).\n",
    "10. Build sdist and wheel distributions for your package (**{numref}`03:Building-your-package`**).\n",
    "\n",
    "    ```\n",
    "    $ poetry build\n",
    "    ```\n",
    "    \n",
    "11. (Optional) Publish your distributions to [TestPyPI](https://test.pypi.org/) and try installing your package (**{numref}`03:Publishing-to-TestPyPI`**).\n",
    "\n",
    "    ```\n",
    "    $ poetry config repositories.test-pypi \\\n",
    "      https://test.pypi.org/legacy/\n",
    "    $ poetry publish -r test-pypi\n",
    "    $ pip install --index-url https://test.pypi.org/simple/ \\\n",
    "      --extra-index-url https://pypi.org/simple \\\n",
    "      pycounts\n",
    "    ```\n",
    "    \n",
    "12. (Optional) Publish your distributions to [PyPI](https://pypi.org/). Your package can now be installed by anyone using `pip` (**{numref}`03:Publishing-to-PyPI`**).\n",
    "\n",
    "    ```\n",
    "    $ poetry publish\n",
    "    $ pip install <pkg-name>\n",
    "    ```\n",
    "    \n",
    "The above workflow uses a particular suite of tools (e.g., `conda`, `poetry`, `sphinx`, etc.) to develop a Python package. While there are other tools that can be used to help build Python packages, the aim of this book is to give a high-level, practical, and efficient introduction to Python packaging using modern tools, and this has influenced our selection of tools in this chapter and book. However, the concepts and workflow discussed here remain relevant to the Python packaging ecosystem, regardless of the exact tools you use to develop your Python packages."
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.6"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": true,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  },
  "toc-autonumbering": false,
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "state": {
     "06e8b80b569b4ac2bb5a989af9695ced": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {
       "height": "350px"
      }
     },
     "07d43b717dd94d33a8eeae066f36d839": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "09294cf0fd6549c78d0f15acf20400b2": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "096724e20b094296bb243e553b2820e6": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "FloatSliderModel",
      "state": {
       "description": "m",
       "layout": "IPY_MODEL_d2ef8aa5fad04047a98f733f89624812",
       "max": 2,
       "min": -2,
       "step": 0.1,
       "style": "IPY_MODEL_d3343e0c83844ad987beb592199dd6f6",
       "value": -0.1
      }
     },
     "0b412fd29e8a44f5bfaaa9a7122a33c3": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "VBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_1da474a760df45c3a290bbf3ba8a7f94",
        "IPY_MODEL_acc0b478d5b748c29c4683cbcfc8e0f3"
       ],
       "layout": "IPY_MODEL_9a4a74191d4b483f8e6ca535c155a33f"
      }
     },
     "107d8e3c4e4249429f4ea5f86f97caa7": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "1455d02e130148c4a24a0943765db04a": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "1574978c1a56499dbe842cb3bf5b9e8f": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "16770b98026442368338a8d27ffceab4": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "1989b160147948d4a6414de3b6c60694": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "1a1f847a77d44da7977d6ed1ae70ac12": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "VBoxModel",
      "state": {
       "_dom_classes": [
        "widget-interact"
       ],
       "children": [
        "IPY_MODEL_884172dbe7194ea7aba24ace80d35049",
        "IPY_MODEL_384a6bfb62454f0bb09a41d14a5eb9c9",
        "IPY_MODEL_8cc71f1ee02d4a10acbda0a6f9cbc4fb"
       ],
       "layout": "IPY_MODEL_da554c291c9f4442991fcf6d620e017e"
      }
     },
     "1cb1287d7a5940829cb770a5553a95b3": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {
       "height": "350px"
      }
     },
     "1da474a760df45c3a290bbf3ba8a7f94": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "IntSliderModel",
      "state": {
       "layout": "IPY_MODEL_323357ab97fc4ed0af4ab5743aa038cc",
       "style": "IPY_MODEL_64e2b864e41244388bcb173e865939f8"
      }
     },
     "1ecb0c5110084d99bd02c6845b36b5f5": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "1ff35f5f7c9543158cd8fcb2fe45e951": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "200d9095bbf14c67af4083eebe042331": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "VBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_ce130e9bb1874109b6e9641a6f6b872a",
        "IPY_MODEL_544ffe02934f47abbe60aa68b1552268"
       ],
       "layout": "IPY_MODEL_2949d658a5e24ae8af8b48f1c68ca710"
      }
     },
     "204835ba5cf741febeb96117f71f03db": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "IntSliderModel",
      "state": {
       "layout": "IPY_MODEL_e788ecb6fbad4de6aac05557dcf109f5",
       "style": "IPY_MODEL_871a273a718c4b7ab105f4bf6f9500e1"
      }
     },
     "20f7461031e44d41bd5b14f4cdd8542a": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "24102f82e9cf49aca85e512f59a1db81": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "262d0a636bf64c67a8b3b7fdb6852569": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "2844ba0a05594aeeb871eaf4fae2c4b9": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "28a33b60ad9947cebcab993a3ecd7b01": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "IntSliderModel",
      "state": {
       "layout": "IPY_MODEL_1989b160147948d4a6414de3b6c60694",
       "style": "IPY_MODEL_f24ec537d93846e48f35cf5bca83e11d",
       "value": 40
      }
     },
     "2949d658a5e24ae8af8b48f1c68ca710": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "2ad8e9ec7f1d4f5f89fadb89c51892cd": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "FloatSliderModel",
      "state": {
       "description": "m",
       "layout": "IPY_MODEL_3679e8d246954e7295c526cec653232b",
       "max": 2,
       "min": -2,
       "step": 0.1,
       "style": "IPY_MODEL_455179b0a10a4d3793163b6270e8c1d4"
      }
     },
     "2be4850df84041778088cca110e64d11": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "HBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_3b84503a8368455bac727f95db5d01d0",
        "IPY_MODEL_5ed1935a5446410aa7103f97d2500695"
       ],
       "layout": "IPY_MODEL_5bc7f1fcf97f41fea3a8623cf73e21a3"
      }
     },
     "2ec147d796d24e06a192384ff4f271d8": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "IntSliderModel",
      "state": {
       "layout": "IPY_MODEL_1ecb0c5110084d99bd02c6845b36b5f5",
       "style": "IPY_MODEL_750974cf708b48bcac5c4b3e178f9e34"
      }
     },
     "323357ab97fc4ed0af4ab5743aa038cc": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "3679e8d246954e7295c526cec653232b": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "373d595c629d4c96ab6c69cabadfbae3": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "384a6bfb62454f0bb09a41d14a5eb9c9": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "FloatSliderModel",
      "state": {
       "description": "b",
       "layout": "IPY_MODEL_79023cd4f95e412d86f33dacb04f9cce",
       "max": 3,
       "min": -3,
       "step": 0.5,
       "style": "IPY_MODEL_da243475d71a41f48bf4855c01052769"
      }
     },
     "3966b0590b414fe1873b43e1835dbc0b": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "VBoxModel",
      "state": {
       "_dom_classes": [
        "widget-interact"
       ],
       "children": [
        "IPY_MODEL_096724e20b094296bb243e553b2820e6",
        "IPY_MODEL_890cbfc7c0f343d2b7991c18b88b8177",
        "IPY_MODEL_fcc376497f9d4b8aa11a102ebc19b6ab"
       ],
       "layout": "IPY_MODEL_ae6272a2743a4752b8798fed0b5dc332"
      }
     },
     "39af2f7e78ff42679117a13c8e3e0c02": {
      "model_module": "@jupyter-widgets/output",
      "model_module_version": "1.0.0",
      "model_name": "OutputModel",
      "state": {
       "layout": "IPY_MODEL_43d437e0efff40b0b7c59c66b9b815f5",
       "outputs": [
        {
         "name": "stdout",
         "output_type": "stream",
         "text": "0 * 0 = 0\n"
        }
       ]
      }
     },
     "3ac4ddb748c447f9903d248db1422cf1": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "HBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_b8aa32ebeb924971ade818f6af335eec",
        "IPY_MODEL_e6a05e8e4f6a4cefb61f5f99b1eb13c2"
       ],
       "layout": "IPY_MODEL_e57657510026435496e5903d5c875cbe"
      }
     },
     "3b84503a8368455bac727f95db5d01d0": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "VBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_ce130e9bb1874109b6e9641a6f6b872a",
        "IPY_MODEL_544ffe02934f47abbe60aa68b1552268"
       ],
       "layout": "IPY_MODEL_20f7461031e44d41bd5b14f4cdd8542a"
      }
     },
     "3ccc38f44df249cd833536b7a8a34327": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "4007d719e1aa45349acb4e9f80f0e4ba": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "VBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_2ec147d796d24e06a192384ff4f271d8",
        "IPY_MODEL_f285911bbe39497a88f0994509363fbb"
       ],
       "layout": "IPY_MODEL_9ecdf012164d45aaa03c28b34bebf7fa"
      }
     },
     "42727846507043a098dc2e932813ee65": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "IntSliderModel",
      "state": {
       "layout": "IPY_MODEL_16770b98026442368338a8d27ffceab4",
       "style": "IPY_MODEL_b1e4ec6cae8d454999896d5ec41be03c"
      }
     },
     "43d437e0efff40b0b7c59c66b9b815f5": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "454c456031894c1498a69aaa4dcb0e97": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "455179b0a10a4d3793163b6270e8c1d4": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "479edca2ec334d01b8af52d21f4c1565": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "494640e15fe7442ebfa3b6786ef64d82": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "4af9e16a11ed4597bbd81a734add4f5c": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "HBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_535d254dd54d41ada990b62be72d5a14",
        "IPY_MODEL_e6a05e8e4f6a4cefb61f5f99b1eb13c2"
       ],
       "layout": "IPY_MODEL_7415264924174df19d8822f16c379f55"
      }
     },
     "4c8b0dc16ddc40af8b131803f5280311": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "HBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_edcfad1763d44bbab957f8d4f981f4e4",
        "IPY_MODEL_d11a21b0488f4970babc1c7ad3f86aa8"
       ],
       "layout": "IPY_MODEL_ef77db303b044fe783f80b51f045e866"
      }
     },
     "4d8487b20ef9419aab03dcc0c813fc77": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "VBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_cf422506005a43978b50beb43766a819",
        "IPY_MODEL_78efe79186ed46c78e9d91400f519f1d"
       ],
       "layout": "IPY_MODEL_ce3b1869747b4502b90f4448204f5d94"
      }
     },
     "4f613b87c7184b80bbc2806c184eb4cb": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "VBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_9053f57dacfc4255ad1dc0b717cbd4be",
        "IPY_MODEL_42727846507043a098dc2e932813ee65"
       ],
       "layout": "IPY_MODEL_8c5220ab1fe748d0abf46067668fd9d1"
      }
     },
     "50f2a1ea21a24ed9a0db566a4678e24e": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "IntSliderModel",
      "state": {
       "layout": "IPY_MODEL_933e89a135cd42e196abb362e0302570",
       "style": "IPY_MODEL_b4ed787903d4445cb7fd4c6827acf9cd"
      }
     },
     "527235c0249e4be68af5cc1b5f6ecaa8": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "535d254dd54d41ada990b62be72d5a14": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "VBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_50f2a1ea21a24ed9a0db566a4678e24e",
        "IPY_MODEL_bfe8c68e54b24762adb625e2d926bc2e"
       ],
       "layout": "IPY_MODEL_d9616e978f2840609e88757e2e7e47f3"
      }
     },
     "544ffe02934f47abbe60aa68b1552268": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "IntSliderModel",
      "state": {
       "layout": "IPY_MODEL_7acc7084e5c34c5a9b18b84c2a78660b",
       "style": "IPY_MODEL_d10b5afbdea647baa88b2156fda8c6c0"
      }
     },
     "54bac03c012745098c9455c5a2325e0a": {
      "model_module": "@jupyter-widgets/output",
      "model_module_version": "1.0.0",
      "model_name": "OutputModel",
      "state": {
       "layout": "IPY_MODEL_1cb1287d7a5940829cb770a5553a95b3",
       "outputs": [
        {
         "data": {
          "image/png": "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\n",
          "text/plain": "<Figure size 432x288 with 1 Axes>"
         },
         "metadata": {
          "needs_background": "light"
         },
         "output_type": "display_data"
        }
       ]
      }
     },
     "57c1af3db3d44cc7bfcfc97614bea646": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "IntSliderModel",
      "state": {
       "layout": "IPY_MODEL_9053341eacf94d139041e6aa7cc93233",
       "style": "IPY_MODEL_527235c0249e4be68af5cc1b5f6ecaa8",
       "value": 40
      }
     },
     "587b16eff8dc4011bcc72b4f4070b14d": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "58a162ea4105481e9d1345fdc79c9f1d": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "5ab9a789ce9a4e97845523051ef0762c": {
      "model_module": "@jupyter-widgets/output",
      "model_module_version": "1.0.0",
      "model_name": "OutputModel",
      "state": {
       "layout": "IPY_MODEL_965ef0e308be48eaa8b8ef19c63bed40",
       "outputs": [
        {
         "name": "stdout",
         "output_type": "stream",
         "text": "0 * 0 = 0\n"
        }
       ]
      }
     },
     "5bc7f1fcf97f41fea3a8623cf73e21a3": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "5d02d239659c496cbb1972806465cbba": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "5d68805b72bd49498579e5c204071464": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "VBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_1da474a760df45c3a290bbf3ba8a7f94",
        "IPY_MODEL_acc0b478d5b748c29c4683cbcfc8e0f3"
       ],
       "layout": "IPY_MODEL_72b02467571a402da6d4414c5e4489fa"
      }
     },
     "5e5877766afd49fcbb63cdbbff1f5684": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "FloatSliderModel",
      "state": {
       "description": "b",
       "layout": "IPY_MODEL_1ff35f5f7c9543158cd8fcb2fe45e951",
       "max": 3,
       "min": -3,
       "step": 0.5,
       "style": "IPY_MODEL_7bc761c122284f8b9822ff186e0f78bd",
       "value": -1
      }
     },
     "5ed1935a5446410aa7103f97d2500695": {
      "model_module": "@jupyter-widgets/output",
      "model_module_version": "1.0.0",
      "model_name": "OutputModel",
      "state": {
       "layout": "IPY_MODEL_262d0a636bf64c67a8b3b7fdb6852569",
       "outputs": [
        {
         "name": "stdout",
         "output_type": "stream",
         "text": "0 * 0 = 0\n"
        }
       ]
      }
     },
     "645724dd1b174799b0ff40a9e6d6ab62": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "HBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_98b7be5be6ad4194bafdff2ffd9a89f0",
        "IPY_MODEL_e5aa35e4f4aa46cf8044c66339de4b96"
       ],
       "layout": "IPY_MODEL_454c456031894c1498a69aaa4dcb0e97"
      }
     },
     "64c7a0f8b8904b1ca743bc4911a341b2": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "64e2b864e41244388bcb173e865939f8": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "6991e87f25be44a78f759d7fd412588b": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "VBoxModel",
      "state": {
       "_dom_classes": [
        "widget-interact"
       ],
       "children": [
        "IPY_MODEL_2ad8e9ec7f1d4f5f89fadb89c51892cd",
        "IPY_MODEL_5e5877766afd49fcbb63cdbbff1f5684",
        "IPY_MODEL_81a51a474122457fa2f9da64776cf5d1"
       ],
       "layout": "IPY_MODEL_8a2b88d881b247d08b03cb1b7183128d"
      }
     },
     "6a03b9b6618c43bb80bcccfc6f7c19f9": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {
       "height": "350px"
      }
     },
     "6b15b70b85584ce18f7b6efe71563f96": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "6c890acf62af426cab22ed65ff429088": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "6ffdc8fcab164aedbf54cf3a45384d64": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "72b02467571a402da6d4414c5e4489fa": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "7415264924174df19d8822f16c379f55": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "750974cf708b48bcac5c4b3e178f9e34": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "78efe79186ed46c78e9d91400f519f1d": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "IntSliderModel",
      "state": {
       "layout": "IPY_MODEL_fdfc64c80c3c407bacf0a395dede228c",
       "style": "IPY_MODEL_8b9cd3fe33484cb794a9c2a4d60caa9a"
      }
     },
     "79023cd4f95e412d86f33dacb04f9cce": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "7a81ba3691dc47449c052dcd9070b618": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "FloatSliderModel",
      "state": {
       "description": "m",
       "layout": "IPY_MODEL_ac55030546d54b0ca4d7e4fbf355a3c8",
       "max": 2,
       "min": -2,
       "step": 0.1,
       "style": "IPY_MODEL_6b15b70b85584ce18f7b6efe71563f96"
      }
     },
     "7acc7084e5c34c5a9b18b84c2a78660b": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "7b066d9aaaa34fcda5803a72047566f0": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "IntSliderModel",
      "state": {
       "layout": "IPY_MODEL_d6b85a8f0ec64ee491848be1b8bf8188",
       "style": "IPY_MODEL_3ccc38f44df249cd833536b7a8a34327",
       "value": 40
      }
     },
     "7bc761c122284f8b9822ff186e0f78bd": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "7c23e7df356f475e994f2f3e70730747": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "7d29dde0dd4046fb9717131bf06b637f": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "FloatSliderModel",
      "state": {
       "description": "b",
       "layout": "IPY_MODEL_b674d805670c4a969daa587ae7d22805",
       "max": 3,
       "min": -3,
       "step": 0.5,
       "style": "IPY_MODEL_494640e15fe7442ebfa3b6786ef64d82",
       "value": -1
      }
     },
     "81a51a474122457fa2f9da64776cf5d1": {
      "model_module": "@jupyter-widgets/output",
      "model_module_version": "1.0.0",
      "model_name": "OutputModel",
      "state": {
       "layout": "IPY_MODEL_94487d6608754721aa3f4401e77dc292",
       "outputs": [
        {
         "data": {
          "image/png": "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\n",
          "text/plain": "<Figure size 432x288 with 1 Axes>"
         },
         "metadata": {
          "needs_background": "light"
         },
         "output_type": "display_data"
        }
       ]
      }
     },
     "856e6ab0a1b14ac490277652b9beabf6": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "HBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_0b412fd29e8a44f5bfaaa9a7122a33c3",
        "IPY_MODEL_5ab9a789ce9a4e97845523051ef0762c"
       ],
       "layout": "IPY_MODEL_f55ac2b80afd433a919cc2d7ab87b098"
      }
     },
     "871a273a718c4b7ab105f4bf6f9500e1": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "884172dbe7194ea7aba24ace80d35049": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "FloatSliderModel",
      "state": {
       "description": "m",
       "layout": "IPY_MODEL_d2e7ead2f8d74c5180cbabbb30b674a8",
       "max": 2,
       "min": -2,
       "step": 0.1,
       "style": "IPY_MODEL_373d595c629d4c96ab6c69cabadfbae3"
      }
     },
     "890cbfc7c0f343d2b7991c18b88b8177": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "FloatSliderModel",
      "state": {
       "description": "b",
       "layout": "IPY_MODEL_24102f82e9cf49aca85e512f59a1db81",
       "max": 3,
       "min": -3,
       "step": 0.5,
       "style": "IPY_MODEL_64c7a0f8b8904b1ca743bc4911a341b2",
       "value": -0.5
      }
     },
     "8a2b88d881b247d08b03cb1b7183128d": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "8b9cd3fe33484cb794a9c2a4d60caa9a": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "8c5220ab1fe748d0abf46067668fd9d1": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "8cc71f1ee02d4a10acbda0a6f9cbc4fb": {
      "model_module": "@jupyter-widgets/output",
      "model_module_version": "1.0.0",
      "model_name": "OutputModel",
      "state": {
       "layout": "IPY_MODEL_6a03b9b6618c43bb80bcccfc6f7c19f9",
       "outputs": [
        {
         "data": {
          "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXIAAAD4CAYAAADxeG0DAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAANZklEQVR4nO3df6xkZ13H8ffHLsUEKrTuQku3uG0kxKIm4E2DgkpoU9paWzFqSqIWa7JBQwIJpmltgih/IRGNEW1WaPxBY6tCpZIS2Eob4x9Ubmt//6ALFula2osoxZCgDV//mLNmejt379ydM/fuV9+v5OaeOeeZ53znOed+5swzM7upKiRJfX3HThcgSVqMQS5JzRnkktScQS5JzRnkktTcrp3Y6e7du2vfvn07sWtJauvOO+/8alXtWb9+R4J83759rK6u7sSuJamtJF+atd6pFUlqziCXpOYMcklqziCXpOYMcklqziCXpOYMcklqziCXpOYMcklqziCXpOYMcklqziCXpOYMcklqziCXpOYMcklqziCXpOYMcklqbrQgT3JCkn9K8omx+pQkbW7MK/J3AA+N2J8kaQ6jBHmSvcBPAB8aoz9J0vzGuiL/PeBK4NsbNUiyP8lqktW1tbWRditJWjjIk1wMPFVVdx6tXVUdqKqVqlrZs2fPoruVJA3GuCJ/HXBJkseAG4A3JvnICP1KkuawcJBX1dVVtbeq9gGXAZ+pqp9fuDJJ0lz8HLkkNbdrzM6q6nbg9jH7lCQdnVfkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzRnkktScQS5JzS0c5EnOSHJbkgeTPJDkHWMUJkmaz64R+ngGeFdV3ZXkJODOJAer6sER+pYkbWLhK/KqeqKq7hqWvwE8BJy+aL+SpPmMOkeeZB/wauCOMfuVJG1stCBP8kLgo8A7q+rpGdv3J1lNsrq2tjbWbiXp/71RgjzJ85iE+PVV9bFZbarqQFWtVNXKnj17xtitJIlxPrUS4MPAQ1X1gcVLkiRtxRhX5K8DfgF4Y5K7h5+LRuhXkjSHhT9+WFX/AGSEWiRJx8BvdkpScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtScwa5JDVnkEtSc6MEeZILkjyS5FCSq8boU5I0n4WDPMkJwAeBC4GzgbckOXvRfiVJ89k1Qh/nAIeq6osASW4ALgUeHKHvZ/nNv32AB//16bG7laRtc/bLvovf+MlXjdrnGFMrpwNfnrr9+LDuWZLsT7KaZHVtbW2E3UqSYJwr8rlU1QHgAMDKykodSx9jP4tJ0v8FY1yRHwbOmLq9d1gnSdoGYwT554BXJDkzyYnAZcDNI/QrSZrDwlMrVfVMkrcDnwJOAK6rqgcWrkySNJdR5sir6hbgljH6kiRtjd/slKTmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJas4gl6TmDHJJam6hIE/y/iQPJ7k3yU1JXjxWYZKk+Sx6RX4Q+P6q+kHg88DVi5ckSdqKhYK8qj5dVc8MNz8L7F28JEnSVow5R34F8MkR+5MkzWHXZg2S3AqcOmPTNVX18aHNNcAzwPVH6Wc/sB/g5S9/+TEVK0l6rk2DvKrOO9r2JG8FLgbOrao6Sj8HgAMAKysrG7aTJG3NpkF+NEkuAK4EfryqvjlOSZKkrVh0jvwPgJOAg0nuTnLtCDVJkrZgoSvyqvresQqRJB0bv9kpSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc2NEuRJ3pWkkuweoz9J0vwWDvIkZwDnA/+yeDmSpK0a44r8d4ErgRqhL0nSFi0U5EkuBQ5X1T1ztN2fZDXJ6tra2iK7lSRN2bVZgyS3AqfO2HQN8OtMplU2VVUHgAMAKysrXr1L0kg2DfKqOm/W+iQ/AJwJ3JMEYC9wV5Jzquoro1YpSdrQpkG+kaq6D3jJkdtJHgNWquqrI9QlSZqTnyOXpOaO+Yp8varaN1ZfkqT5eUUuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUnEEuSc0Z5JLUXKq2//9BTrIGfOkY774bOB7/Oznr2hrr2hrr2prjtS5YrLbvqao961fuSJAvIslqVa3sdB3rWdfWWNfWWNfWHK91wXJqc2pFkpozyCWpuY5BfmCnC9iAdW2NdW2NdW3N8VoXLKG2dnPkkqRn63hFLkmaYpBLUnPHZZAn+dkkDyT5dpKVdduuTnIoySNJ3rTB/c9McsfQ7sYkJy6hxhuT3D38PJbk7g3aPZbkvqHd6th1zNjfe5Icnqrtog3aXTCM4aEkV21DXe9P8nCSe5PclOTFG7TblvHa7PEnef5wjA8N59K+ZdUytc8zktyW5MHh/H/HjDZvSPL1qeP77mXXNez3qMclE78/jNe9SV6zDTW9cmoc7k7ydJJ3rmuzbeOV5LokTyW5f2rdKUkOJnl0+H3yBve9fGjzaJLLt7zzqjrufoDvA14J3A6sTK0/G7gHeD5wJvAF4IQZ9/9L4LJh+VrgV5Zc7+8A795g22PA7m0cu/cAv7ZJmxOGsTsLOHEY07OXXNf5wK5h+X3A+3ZqvOZ5/MCvAtcOy5cBN27DsTsNeM2wfBLw+Rl1vQH4xHadT/MeF+Ai4JNAgNcCd2xzfScAX2HyhZkdGS/gx4DXAPdPrftt4Kph+apZ5z1wCvDF4ffJw/LJW9n3cXlFXlUPVdUjMzZdCtxQVd+qqn8GDgHnTDdIEuCNwF8Pq/4U+Kll1Trs7+eAv1jWPpbgHOBQVX2xqv4LuIHJ2C5NVX26qp4Zbn4W2LvM/W1insd/KZNzBybn0rnDsV6aqnqiqu4alr8BPAScvsx9juhS4M9q4rPAi5Octo37Pxf4QlUd6zfGF1ZVfw98bd3q6fNooyx6E3Cwqr5WVf8OHAQu2Mq+j8sgP4rTgS9P3X6c557o3w38x1RozGozph8FnqyqRzfYXsCnk9yZZP8S65j29uHl7XUbvJSbZxyX6QomV2+zbMd4zfP4/7fNcC59ncm5tS2GqZxXA3fM2PzDSe5J8skkr9qmkjY7Ljt9Tl3GxhdTOzFeR7y0qp4Ylr8CvHRGm4XHbtex1ba4JLcCp87YdE1VfXy765llzhrfwtGvxl9fVYeTvAQ4mOTh4Zl7KXUBfwS8l8kf3nuZTPtcscj+xqjryHgluQZ4Brh+g25GH69ukrwQ+Cjwzqp6et3mu5hMH/zn8P7H3wCv2IayjtvjMrwHdglw9YzNOzVez1FVlWQpn/fesSCvqvOO4W6HgTOmbu8d1k37NyYv63YNV1Kz2oxSY5JdwE8DP3SUPg4Pv59KchOTl/UL/QHMO3ZJ/hj4xIxN84zj6HUleStwMXBuDZODM/oYfbxmmOfxH2nz+HCcX8Tk3FqqJM9jEuLXV9XH1m+fDvaquiXJHybZXVVL/Qei5jguSzmn5nQhcFdVPbl+w06N15Qnk5xWVU8MU01PzWhzmMlc/hF7mbw/OLduUys3A5cNnyg4k8kz6z9ONxgC4jbgZ4ZVlwPLusI/D3i4qh6ftTHJC5KcdGSZyRt+989qO5Z185Jv3mB/nwNekcmne05k8rL05iXXdQFwJXBJVX1zgzbbNV7zPP6bmZw7MDmXPrPRk89Yhjn4DwMPVdUHNmhz6pG5+iTnMPkbXuoTzJzH5WbgF4dPr7wW+PrUlMKybfiqeCfGa53p82ijLPoUcH6Sk4ep0POHdfPbjndzj+Hd3zczmSf6FvAk8Kmpbdcw+cTBI8CFU+tvAV42LJ/FJOAPAX8FPH9Jdf4J8LZ1614G3DJVxz3DzwNMphiWPXZ/DtwH3DucRKetr2u4fRGTT0V8YZvqOsRkHvDu4efa9XVt53jNevzAbzF5ogH4zuHcOTScS2dtwxi9nsmU2L1T43QR8LYj5xnw9mFs7mHypvGPbENdM4/LuroCfHAYz/uY+rTZkmt7AZNgftHUuh0ZLyZPJk8A/z3k1y8zeV/l74BHgVuBU4a2K8CHpu57xXCuHQJ+aav79iv6ktRct6kVSdI6BrkkNWeQS1JzBrkkNWeQS1JzBrkkNWeQS1Jz/wOt0eLL/K7pHwAAAABJRU5ErkJggg==\n",
          "text/plain": "<Figure size 432x288 with 1 Axes>"
         },
         "metadata": {
          "needs_background": "light"
         },
         "output_type": "display_data"
        }
       ]
      }
     },
     "8dda8bf6c916451e91b96e56fc4d2a76": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "9053341eacf94d139041e6aa7cc93233": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "9053f57dacfc4255ad1dc0b717cbd4be": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "IntSliderModel",
      "state": {
       "layout": "IPY_MODEL_ef44415ff8dd456681965184a2a299e8",
       "style": "IPY_MODEL_cc29a198d1d44797a909ea9a68a5d484"
      }
     },
     "907bd0f286754766afa0d6ba1612d301": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "HBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_4d8487b20ef9419aab03dcc0c813fc77",
        "IPY_MODEL_d11a21b0488f4970babc1c7ad3f86aa8"
       ],
       "layout": "IPY_MODEL_1574978c1a56499dbe842cb3bf5b9e8f"
      }
     },
     "90a70b5f560f437f9bb9422a4a6f6592": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "933e89a135cd42e196abb362e0302570": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "935fac04b32e4e0b9e1f9ea118a04ee1": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "IntSliderModel",
      "state": {
       "layout": "IPY_MODEL_b9e169b8efac4eeb990ae37db1e8de12",
       "max": 200,
       "style": "IPY_MODEL_9927296f85f248858bdc0a0abcc67e94",
       "value": 100
      }
     },
     "94487d6608754721aa3f4401e77dc292": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {
       "height": "350px"
      }
     },
     "965ef0e308be48eaa8b8ef19c63bed40": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "97c973da7b714095959a2a66d7d28c65": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "98b7be5be6ad4194bafdff2ffd9a89f0": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "VBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_b12e150681c94126b68be84b0d7cf158",
        "IPY_MODEL_204835ba5cf741febeb96117f71f03db"
       ],
       "layout": "IPY_MODEL_7c23e7df356f475e994f2f3e70730747"
      }
     },
     "9927296f85f248858bdc0a0abcc67e94": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "9a4a74191d4b483f8e6ca535c155a33f": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "9c4a507b139f440fabaa95140d4b2193": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "9ecdf012164d45aaa03c28b34bebf7fa": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "a5e552b00bc54d1991d64d1e61258563": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "VBoxModel",
      "state": {
       "_dom_classes": [
        "widget-interact"
       ],
       "children": [
        "IPY_MODEL_7a81ba3691dc47449c052dcd9070b618",
        "IPY_MODEL_7d29dde0dd4046fb9717131bf06b637f",
        "IPY_MODEL_54bac03c012745098c9455c5a2325e0a"
       ],
       "layout": "IPY_MODEL_2844ba0a05594aeeb871eaf4fae2c4b9"
      }
     },
     "ac55030546d54b0ca4d7e4fbf355a3c8": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "acc0b478d5b748c29c4683cbcfc8e0f3": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "IntSliderModel",
      "state": {
       "layout": "IPY_MODEL_c69f7b00b5a54f60908cb3d92021daf6",
       "style": "IPY_MODEL_479edca2ec334d01b8af52d21f4c1565"
      }
     },
     "ae6272a2743a4752b8798fed0b5dc332": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "b070d7bd6a794d2ba0242e4220af368f": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "b12e150681c94126b68be84b0d7cf158": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "IntSliderModel",
      "state": {
       "layout": "IPY_MODEL_1455d02e130148c4a24a0943765db04a",
       "style": "IPY_MODEL_c2bcf8f8ff7b4623a8311427461ec26b"
      }
     },
     "b1e4ec6cae8d454999896d5ec41be03c": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "b4ed787903d4445cb7fd4c6827acf9cd": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "b65b837d3b614513bef7014f6e97b8be": {
      "model_module": "@jupyter-widgets/output",
      "model_module_version": "1.0.0",
      "model_name": "OutputModel",
      "state": {
       "layout": "IPY_MODEL_d9226ab50b4a4df9bdd6574f239a76a5",
       "outputs": [
        {
         "name": "stdout",
         "output_type": "stream",
         "text": "0 * 0 = 0\n"
        }
       ]
      }
     },
     "b674d805670c4a969daa587ae7d22805": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "b8aa32ebeb924971ade818f6af335eec": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "VBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_50f2a1ea21a24ed9a0db566a4678e24e",
        "IPY_MODEL_bfe8c68e54b24762adb625e2d926bc2e"
       ],
       "layout": "IPY_MODEL_587b16eff8dc4011bcc72b4f4070b14d"
      }
     },
     "b9e169b8efac4eeb990ae37db1e8de12": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "bba860475dc34aa295f004356f4c7e70": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "HBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_4f613b87c7184b80bbc2806c184eb4cb",
        "IPY_MODEL_b65b837d3b614513bef7014f6e97b8be"
       ],
       "layout": "IPY_MODEL_f128f679eeb840f88c63d5fb65e08abe"
      }
     },
     "be52a81fc3e44efc8f0d70d81da55763": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "HBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_4007d719e1aa45349acb4e9f80f0e4ba",
        "IPY_MODEL_39af2f7e78ff42679117a13c8e3e0c02"
       ],
       "layout": "IPY_MODEL_90a70b5f560f437f9bb9422a4a6f6592"
      }
     },
     "bf592509273c491c9496cc5bce017755": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "HBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_200d9095bbf14c67af4083eebe042331",
        "IPY_MODEL_5ed1935a5446410aa7103f97d2500695"
       ],
       "layout": "IPY_MODEL_b070d7bd6a794d2ba0242e4220af368f"
      }
     },
     "bfe8c68e54b24762adb625e2d926bc2e": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "IntSliderModel",
      "state": {
       "layout": "IPY_MODEL_09294cf0fd6549c78d0f15acf20400b2",
       "style": "IPY_MODEL_d5700c1140ff4a598008ed28c45f7d25"
      }
     },
     "c2bcf8f8ff7b4623a8311427461ec26b": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "c2cc4c190c324cc0a0ce9bf781a499eb": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "c69f7b00b5a54f60908cb3d92021daf6": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "cc29a198d1d44797a909ea9a68a5d484": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "cd9593419907498e8a80fa9fb7850749": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "ce130e9bb1874109b6e9641a6f6b872a": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "IntSliderModel",
      "state": {
       "layout": "IPY_MODEL_c2cc4c190c324cc0a0ce9bf781a499eb",
       "style": "IPY_MODEL_cd9593419907498e8a80fa9fb7850749"
      }
     },
     "ce3b1869747b4502b90f4448204f5d94": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "cf422506005a43978b50beb43766a819": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "IntSliderModel",
      "state": {
       "layout": "IPY_MODEL_9c4a507b139f440fabaa95140d4b2193",
       "style": "IPY_MODEL_107d8e3c4e4249429f4ea5f86f97caa7"
      }
     },
     "d10b5afbdea647baa88b2156fda8c6c0": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "d11a21b0488f4970babc1c7ad3f86aa8": {
      "model_module": "@jupyter-widgets/output",
      "model_module_version": "1.0.0",
      "model_name": "OutputModel",
      "state": {
       "layout": "IPY_MODEL_97c973da7b714095959a2a66d7d28c65",
       "outputs": [
        {
         "name": "stdout",
         "output_type": "stream",
         "text": "0 * 0 = 0\n"
        }
       ]
      }
     },
     "d2a772f4a9db458db7a47d77c13dd3d5": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "IntSliderModel",
      "state": {
       "layout": "IPY_MODEL_6ffdc8fcab164aedbf54cf3a45384d64",
       "style": "IPY_MODEL_e0dea1c96cc34a53938b3f0c52e09fe1",
       "value": 40
      }
     },
     "d2e7ead2f8d74c5180cbabbb30b674a8": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "d2ef8aa5fad04047a98f733f89624812": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "d3343e0c83844ad987beb592199dd6f6": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "d5700c1140ff4a598008ed28c45f7d25": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "d67190fd827a4b1a8ca8079df64d0a0a": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "d6b85a8f0ec64ee491848be1b8bf8188": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "d9226ab50b4a4df9bdd6574f239a76a5": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "d9616e978f2840609e88757e2e7e47f3": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "da243475d71a41f48bf4855c01052769": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "da554c291c9f4442991fcf6d620e017e": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "e0dea1c96cc34a53938b3f0c52e09fe1": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "e57657510026435496e5903d5c875cbe": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "e5aa35e4f4aa46cf8044c66339de4b96": {
      "model_module": "@jupyter-widgets/output",
      "model_module_version": "1.0.0",
      "model_name": "OutputModel",
      "state": {
       "layout": "IPY_MODEL_fc608307f2064139ade48955ecbaf7e9",
       "outputs": [
        {
         "name": "stdout",
         "output_type": "stream",
         "text": "0 * 0 = 0\n"
        }
       ]
      }
     },
     "e6a05e8e4f6a4cefb61f5f99b1eb13c2": {
      "model_module": "@jupyter-widgets/output",
      "model_module_version": "1.0.0",
      "model_name": "OutputModel",
      "state": {
       "layout": "IPY_MODEL_d67190fd827a4b1a8ca8079df64d0a0a",
       "outputs": [
        {
         "name": "stdout",
         "output_type": "stream",
         "text": "0 * 0 = 0\n"
        }
       ]
      }
     },
     "e788ecb6fbad4de6aac05557dcf109f5": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "ea2be2adfc72403eb1236220a37dfd36": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "HBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_5d68805b72bd49498579e5c204071464",
        "IPY_MODEL_5ab9a789ce9a4e97845523051ef0762c"
       ],
       "layout": "IPY_MODEL_6c890acf62af426cab22ed65ff429088"
      }
     },
     "ec1db3073c594dac8b081d2ad5e072e1": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "IntSliderModel",
      "state": {
       "layout": "IPY_MODEL_5d02d239659c496cbb1972806465cbba",
       "style": "IPY_MODEL_58a162ea4105481e9d1345fdc79c9f1d",
       "value": 40
      }
     },
     "edcfad1763d44bbab957f8d4f981f4e4": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "VBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_cf422506005a43978b50beb43766a819",
        "IPY_MODEL_78efe79186ed46c78e9d91400f519f1d"
       ],
       "layout": "IPY_MODEL_fd89962cc1214dd7afd0ee8f6c352b76"
      }
     },
     "ef44415ff8dd456681965184a2a299e8": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "ef77db303b044fe783f80b51f045e866": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "f128f679eeb840f88c63d5fb65e08abe": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "f24ec537d93846e48f35cf5bca83e11d": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "SliderStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "f285911bbe39497a88f0994509363fbb": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.5.0",
      "model_name": "IntSliderModel",
      "state": {
       "layout": "IPY_MODEL_07d43b717dd94d33a8eeae066f36d839",
       "style": "IPY_MODEL_8dda8bf6c916451e91b96e56fc4d2a76"
      }
     },
     "f55ac2b80afd433a919cc2d7ab87b098": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "fc608307f2064139ade48955ecbaf7e9": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "fcc376497f9d4b8aa11a102ebc19b6ab": {
      "model_module": "@jupyter-widgets/output",
      "model_module_version": "1.0.0",
      "model_name": "OutputModel",
      "state": {
       "layout": "IPY_MODEL_06e8b80b569b4ac2bb5a989af9695ced",
       "outputs": [
        {
         "data": {
          "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXIAAAD4CAYAAADxeG0DAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjEsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+j8jraAAAPSklEQVR4nO3dfYwc9X3H8c8nPgP14WDjs4HYmDsEikrTSqEnN23SNgJCiItwU7WVW7UhpZKVVkggpUJQS1HU/NM0avqgpkUOQekDKjQPFBdBwTSgqqognF3bgA3B4HOwMWCDgRRTGodv/5g5upx373ZvZ3b3e/d+SSfvzszN/G52/L652d07R4QAAHm9p98DAAB0h5ADQHKEHACSI+QAkBwhB4Dkhvqx0ZGRkRgdHe3HpgEgre3btx+NiJXTp/cl5KOjo5qYmOjHpgEgLdsHmk3n0goAJEfIASA5Qg4AyRFyAEiOkANAcoQcAJIj5ACQHCEHgOQIOQAkR8gBIDlCDgDJEXIASI6QA0ByhBwAkiPkAJAcIQeA5Ag5ACRXWchtL7L9X7bvrmqdAIDZVXlGfp2kvRWuDwDQhkpCbnuNpF+SdEsV6wMAtK+qM/I/l3SDpLdbLWB7k+0J2xNHjhypaLMAgK5DbvtKSS9FxPaZlouILRExHhHjK1eu7HazAIBSFWfkH5Z0le1JSbdLusT2P1SwXgBAG7oOeUTcFBFrImJU0kZJ34mI3+p6ZACAtvA6cgBIbqjKlUXEQ5IeqnKdAICZcUYOAMkRcgBIjpADQHKEHACSI+QAkBwhB4DkCDkAJEfIASA5Qg4AyRFyAEiOkANAcoQcAJIj5ACQHCEHgOQIOQAkR8gBIDlCDgDJEXIASI6QA0ByhBwAkiPkAJAcIQeA5Ag5ACRHyAEgOUIOAMkRcgBIjpADQHKEHACSI+QAkBwhB4DkCDkAJEfIASA5Qg4AyRFyAEiu65DbPtf2g7b32H7C9nVVDAwA0J6hCtZxQtJnI2KH7aWSttveFhF7Klg3AGAWXZ+RR8ThiNhR3v6BpL2SVne7XgBAeyq9Rm57VNIHJT1S5XoBAK1VFnLbp0v6lqTrI+L1JvM32Z6wPXHkyJGqNgsAC14lIbe9WEXEb4uIbzdbJiK2RMR4RIyvXLmyis0CAFTNq1Ys6WuS9kbEl7sfEgCgE1WckX9Y0m9LusT2zvJjfQXrBQC0oeuXH0bEf0hyBWMBAMwB7+wEgOQIOQAkR8gBIDlCDgDJEXIASI6QA0ByhBwAkiPkAJAcIQeA5Ag5ACRHyAEgOUIOAMkRcgBIjpADQHKEHACSI+QAkBwhB4DkCDkAJEfIASA5Qg4AyRFyAEiOkANAcoQcAJIj5ACQHCEHgOQIOQAkN9TvAXTiXx9/QXsOv67RFUs0OjKs0RXDWr5ksWz3e2gA0DepQv7d/a/o6/+5X2/H/09772lDGhsZ1nkrhsu4F5EfWzGsZUQewALgiJh9qYqNj4/HxMTEnD73rRM/0nOvvKkDL7+h/Uff0IGXj2uyvP38q28SeQDzlu3tETE+fXqqM3JJOnVokS5YdbouWHX6SfNaRX7H94/p7t3PE3kA81K6kM+EyANYiOZVyGdSV+THRpbovBVEHkD/LJiQz6STyE++XISeyAMYFIR8FkQewKAj5F2oOvKjZeiJPIBOVBJy21dI+gtJiyTdEhF/XMV6M5tL5LcfOKZ/2UXkAXSm65DbXiTpK5I+JumgpEdtb42IPd2ue75qJ/KTZeCJPIDZVHFGvk7Svoh4VpJs3y5pgyRCPge9iPzy4VN6+BUBqFsVIV8t6bmG+wcl/cz0hWxvkrRJktauXVvBZheeuUZ+667n1fgG3jN+bPE7r40n8kB+PXuyMyK2SNoiFW/R79V2FwoiDyxcVYT8kKRzG+6vKadhQBB5YH6rIuSPSrrQ9piKgG+U9JsVrBc9UGfkR1eUv2qYyAO16jrkEXHC9rWS7lPx8sNbI+KJrkeGviPyQA7pfo0tBl+zyE8eLX5/zaFX3yTywBzNm19ji8HX6Zn85FHO5IFuEHL0VB2RH10xrFEijwWMkGNgEHlgbgg5UiDyQGuEHOkReSx0hBzzWqWRn/qzf0QeA4aQY8GaPfLH33nZJJHHICPkQBNF5JfqglVLT5pH5DFoCDnQobojPzYyrGVLiDzaR8iBChF59AMhB3pkLpGfmCTymB0hBwYAkUc3CDkw4OqK/NjIsM5bsYTIzwOEHEisk8jvP1r8qmEiP/8QcmCeIvILByEHFiAiP78QcgDvUnXkx1aUf9uVyNeGkANoWzuR33/0uA40RP7RyWO6i8jXipADqASR7x9CDqB2VUV+2ZLFRdyJ/LsQcgB9ReS7R8gBDKyZIv8/P/yRDh4j8hIhB5DUaYuJ/BRCDmDeqTPyU6+bH6TIE3IAC0qnkZ98+Y2BjzwhB4BS1sgTcgBoQ1WR/+qnxvWxi86qdGyEHAC61EnkP7D6vZVvn5ADQI1minxV3lPbmgEAPUHIASA5Qg4AyRFyAEiOkANAcl2F3PaXbD9pe7ftO20vq2pgAID2dHtGvk3SByLipyR9T9JN3Q8JANCJrkIeEfdHxIny7sOS1nQ/JABAJ6q8Rn6NpHsrXB8AoA2zvrPT9gOSzm4ya3NE3FUus1nSCUm3zbCeTZI2SdLatWvnNFgAwMlmDXlEXDbTfNuflnSlpEsjGn81zEnr2SJpiySNj4+3XA4A0JmufteK7Ssk3SDpFyPieDVDAgB0ottr5H8laamkbbZ32r65gjEBADrQ1Rl5RFxQ1UAAAHPDOzsBIDlCDgDJEXIASI6QA0ByhBwAkiPkAJAcIQeA5Ag5ACRHyAEgOUIOAMkRcgBIjpADQHKEHACSI+QAkBwhB4DkCDkAJEfIASA5Qg4AyRFyAEiOkANAcoQcAJIj5ACQHCEHgOQIOQAkR8gBIDlCDgDJEXIASI6QA0ByhBwAkiPkAJAcIQeA5Ag5ACRHyAEgOUIOAMkRcgBIrpKQ2/6s7bA9UsX6AADt6zrkts+VdLmk73c/HABAp6o4I/8zSTdIigrWBQDoUFcht71B0qGI2NXGsptsT9ieOHLkSDebBQA0GJptAdsPSDq7yazNkv5QxWWVWUXEFklbJGl8fJyzdwCoyKwhj4jLmk23/ZOSxiTtsi1JayTtsL0uIl6odJQAgJZmDXkrEfGYpFVT921PShqPiKMVjAsA0CZeRw4Ayc35jHy6iBital0AgPZxRg4AyRFyAEiOkANAcoQcAJIj5ACQHCEHgOQIOQAkR8gBIDlCDgDJEXIASI6QA0ByhBwAkiPkAJAcIQeA5Ag5ACRHyAEgOUf0/u8g2z4i6cAcP31E0iD+OTnG1RnG1RnG1ZlBHZfU3djOi4iV0yf2JeTdsD0REeP9Hsd0jKszjKszjKszgzouqZ6xcWkFAJIj5ACQXMaQb+n3AFpgXJ1hXJ1hXJ0Z1HFJNYwt3TVyAMC7ZTwjBwA0IOQAkNxAhtz2r9l+wvbbtsenzbvJ9j7bT9n+eIvPH7P9SLncHbZPqWGMd9jeWX5M2t7ZYrlJ24+Vy01UPY4m2/u87UMNY1vfYrkryn24z/aNPRjXl2w/aXu37TttL2uxXE/212xfv+1Ty8d4X3ksjdY1loZtnmv7Qdt7yuP/uibLfNT2aw2P7+fqHle53RkfFxf+stxfu21f3IMxvb9hP+y0/brt66ct07P9ZftW2y/Zfrxh2pm2t9l+uvx3eYvPvbpc5mnbV3e88YgYuA9JPy7p/ZIekjTeMP0iSbsknSppTNIzkhY1+fx/krSxvH2zpN+rebx/KulzLeZNShrp4b77vKQ/mGWZReW+O1/SKeU+vajmcV0uaai8/UVJX+zX/mrn65f0+5JuLm9vlHRHDx67cyRdXN5eKul7Tcb1UUl39+p4avdxkbRe0r2SLOlDkh7p8fgWSXpBxRtm+rK/JP2CpIslPd4w7U8k3VjevrHZcS/pTEnPlv8uL28v72TbA3lGHhF7I+KpJrM2SLo9It6KiP2S9kla17iAbUu6RNI3y0l/K+mX6xprub1fl/SPdW2jBusk7YuIZyPifyXdrmLf1iYi7o+IE+XdhyWtqXN7s2jn69+g4tiRimPp0vKxrk1EHI6IHeXtH0jaK2l1ndus0AZJfxeFhyUts31OD7d/qaRnImKu7xjvWkT8u6RXpk1uPI5atejjkrZFxCsRcUzSNklXdLLtgQz5DFZLeq7h/kGdfKCvkPRqQzSaLVOln5f0YkQ83WJ+SLrf9nbbm2ocR6Nryx9vb23xo1w7+7FO16g4e2umF/urna//nWXKY+k1FcdWT5SXcj4o6ZEms3/W9i7b99r+iR4NabbHpd/H1Ea1Ppnqx/6aclZEHC5vvyDprCbLdL3vhuY2tu7ZfkDS2U1mbY6Iu3o9nmbaHONvaOaz8Y9ExCHbqyRts/1k+Z27lnFJ+htJX1DxH+8LKi77XNPN9qoY19T+sr1Z0glJt7VYTeX7Kxvbp0v6lqTrI+L1abN3qLh88N/l8x//LOnCHgxrYB+X8jmwqyTd1GR2v/bXSSIibNfyeu++hTwiLpvDpx2SdG7D/TXltEYvq/ixbqg8k2q2TCVjtD0k6Vck/fQM6zhU/vuS7TtV/Fjf1X+Adved7a9KurvJrHb2Y+Xjsv1pSVdKujTKi4NN1lH5/mqina9/apmD5eN8hopjq1a2F6uI+G0R8e3p8xvDHhH32P5r2yMRUesviGrjcanlmGrTJyTtiIgXp8/o1/5q8KLtcyLicHmp6aUmyxxScS1/yhoVzw+2Ldulla2SNpavKBhT8Z31u40LlIF4UNKvlpOullTXGf5lkp6MiIPNZtoetr106raKJ/web7ZsVaZdl/xki+09KulCF6/uOUXFj6Vbax7XFZJukHRVRBxvsUyv9lc7X/9WFceOVBxL32n1zacq5TX4r0naGxFfbrHM2VPX6m2vU/F/uNZvMG0+Llslfap89cqHJL3WcEmhbi1/Ku7H/pqm8Thq1aL7JF1ue3l5KfTyclr7evFs7hye/f2kiutEb0l6UdJ9DfM2q3jFwVOSPtEw/R5J7ytvn68i8PskfUPSqTWN8+uSPjNt2vsk3dMwjl3lxxMqLjHUve/+XtJjknaXB9E508dV3l+v4lURz/RoXPtUXAfcWX7cPH1cvdxfzb5+SX+k4huNJJ1WHjv7ymPp/B7so4+ouCS2u2E/rZf0manjTNK15b7ZpeJJ45/rwbiaPi7TxmVJXyn352NqeLVZzWMbVhHmMxqm9WV/qfhmcljSD8t+/a6K51X+TdLTkh6QdGa57LikWxo+95ryWNsn6Xc63TZv0QeA5LJdWgEATEPIASA5Qg4AyRFyAEiOkANAcoQcAJIj5ACQ3P8BjBRvaVqOFr4AAAAASUVORK5CYII=\n",
          "text/plain": "<Figure size 432x288 with 1 Axes>"
         },
         "metadata": {
          "needs_background": "light"
         },
         "output_type": "display_data"
        }
       ]
      }
     },
     "fd89962cc1214dd7afd0ee8f6c352b76": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "fdfc64c80c3c407bacf0a395dede228c": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.2.0",
      "model_name": "LayoutModel",
      "state": {}
     }
    },
    "version_major": 2,
    "version_minor": 0
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}