├── .prettierrc.toml ├── teaching.md ├── _static ├── share.png ├── images │ ├── docsets.png │ ├── maslow.png │ ├── navigate.png │ ├── black-panther.jpeg │ ├── dash-docsets-repo.png │ ├── hidden-oss-work.png │ ├── extract-hidden-value.png │ ├── google-domains-static-ip.png │ ├── google-domains-dynamic-dns.png │ ├── google-domains-acme-challenge.png │ └── google-domains-wildcard-subdomain.png ├── anderson-profile.jpeg └── custom.css ├── posts.md ├── .gitattributes ├── feed.xml ├── .github ├── dependabot.yml └── workflows │ └── ci.yaml ├── talks.md ├── .binder └── environment.yml ├── posts ├── 2019 │ └── scipy-2019.md ├── 2020 │ ├── sphinx-based-redesign.md │ ├── maurice-hilleman.md │ ├── the-sun-and-her-scorch.md │ ├── chadwick-boseman.md │ ├── a-knitting-weekend-with-dash.md │ ├── advent-of-code-day-6.ipynb │ ├── advent-of-code-day-5.ipynb │ ├── advent-of-code-day-9.ipynb │ ├── advent-of-code-day-3.ipynb │ ├── advent-of-code-day-2.ipynb │ └── advent-of-code-day-8.ipynb ├── 2021 │ ├── freedom-of-cryptography.md │ ├── mesdames.md │ ├── lets-encrypt-wildcard-ssl-certificate-on-centos.md │ ├── google-dynamic-dns-wildcard-subdomains.md │ ├── how-to-merge-disk-partitions-on-centos.md │ ├── dictionary-merge-and-update-operators.ipynb │ └── function-expressions-in-js.ipynb ├── 2022 │ └── multitude.md └── drafts │ └── bayesian-for-hackers │ └── probability-distributions.ipynb ├── .pre-commit-config.yaml ├── environment.yml ├── Makefile ├── _templates └── hello.html ├── README.md ├── index.md ├── pyproject.toml ├── config_data ├── teaching.yaml └── talks.yaml ├── .gitignore ├── data └── advent-of-code │ └── 2020 │ ├── day-1-input │ ├── day-8-input │ ├── day-3-input │ ├── day-5-input │ └── day-9-input ├── about.md ├── talks_gallery.md ├── teaching_gallery.md └── conf.py /.prettierrc.toml: -------------------------------------------------------------------------------- 1 | tabWidth = 2 2 | semi = false 3 | singleQuote = true 4 | -------------------------------------------------------------------------------- /teaching.md: -------------------------------------------------------------------------------- 1 | # Teaching 2 | 3 | ```{include} teaching_gallery.md 4 | 5 | ``` 6 | -------------------------------------------------------------------------------- /_static/share.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersy005/blog/main/_static/share.png -------------------------------------------------------------------------------- /posts.md: -------------------------------------------------------------------------------- 1 | # ✍🏽 Posts 2 | 3 | % This will be replaced by `ablog` so there's nothing here. 4 | -------------------------------------------------------------------------------- /_static/images/docsets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersy005/blog/main/_static/images/docsets.png -------------------------------------------------------------------------------- /_static/images/maslow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersy005/blog/main/_static/images/maslow.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=auto 3 | -------------------------------------------------------------------------------- /_static/images/navigate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersy005/blog/main/_static/images/navigate.png -------------------------------------------------------------------------------- /_static/anderson-profile.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersy005/blog/main/_static/anderson-profile.jpeg -------------------------------------------------------------------------------- /_static/images/black-panther.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersy005/blog/main/_static/images/black-panther.jpeg -------------------------------------------------------------------------------- /_static/images/dash-docsets-repo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersy005/blog/main/_static/images/dash-docsets-repo.png -------------------------------------------------------------------------------- /_static/images/hidden-oss-work.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersy005/blog/main/_static/images/hidden-oss-work.png -------------------------------------------------------------------------------- /_static/images/extract-hidden-value.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersy005/blog/main/_static/images/extract-hidden-value.png -------------------------------------------------------------------------------- /feed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | https://blog.andersonbanihirwe.dev/posts/atom.xml 4 | 5 | 6 | -------------------------------------------------------------------------------- /_static/images/google-domains-static-ip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersy005/blog/main/_static/images/google-domains-static-ip.png -------------------------------------------------------------------------------- /_static/images/google-domains-dynamic-dns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersy005/blog/main/_static/images/google-domains-dynamic-dns.png -------------------------------------------------------------------------------- /_static/images/google-domains-acme-challenge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersy005/blog/main/_static/images/google-domains-acme-challenge.png -------------------------------------------------------------------------------- /_static/images/google-domains-wildcard-subdomain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersy005/blog/main/_static/images/google-domains-wildcard-subdomain.png -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | groups: 8 | actions: 9 | patterns: 10 | - "*" 11 | -------------------------------------------------------------------------------- /talks.md: -------------------------------------------------------------------------------- 1 | # 🎙 Talks 2 | 3 | I’ve given talks at academic and software conferences. 4 | Some of these are recorded and available online. 5 | Below are a few highlighted talks that I have given recently. 6 | 7 | ```{include} talks_gallery.md 8 | 9 | ``` 10 | -------------------------------------------------------------------------------- /.binder/environment.yml: -------------------------------------------------------------------------------- 1 | name: blog 2 | channels: 3 | - conda-forge 4 | dependencies: 5 | - ablog 6 | - jupyterlab 7 | - myst-nb 8 | - pandas 9 | - pydata-sphinx-theme 10 | - python=3.8 11 | - sphinx-panels 12 | - pip 13 | - pip: 14 | - sphinxext-opengraph 15 | - sphinxcontrib-bibtex 16 | -------------------------------------------------------------------------------- /posts/2020/sphinx-based-redesign.md: -------------------------------------------------------------------------------- 1 | --- 2 | author: Anderson Banihirwe 3 | date: 2020-11-30 4 | tags: news 5 | --- 6 | 7 | # Migrating from `fastpages` to sphinx-based blog 8 | 9 | After a few months on [fastpages](https://fastpages.fast.ai/) hosted on GitHub pages, all the content I have published on my blog is now being built with sphinx hosted on Github pages. This sphinx-based redesign was inspired by [this blog post by Chris Holdgraf](https://predictablynoisy.com/posts/2020/sphinx-blogging/). Thank you 🙏🏽, Chris! 10 | 11 | The Sphinx source for this blog is hosted on Github in this [repo](https://github.com/andersy005/blog). 12 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | ci: 2 | autoupdate_schedule: monthly 3 | repos: 4 | - repo: https://github.com/pre-commit/pre-commit-hooks 5 | rev: v5.0.0 6 | hooks: 7 | - id: trailing-whitespace 8 | - id: end-of-file-fixer 9 | - id: check-docstring-first 10 | - id: check-json 11 | - id: check-yaml 12 | - id: double-quote-string-fixer 13 | - id: debug-statements 14 | - id: mixed-line-ending 15 | 16 | - repo: https://github.com/astral-sh/ruff-pre-commit 17 | rev: "v0.7.2" 18 | hooks: 19 | - id: ruff 20 | args: ["--fix"] 21 | - id: ruff-format 22 | 23 | - repo: https://github.com/pre-commit/mirrors-prettier 24 | rev: v4.0.0-alpha.8 25 | hooks: 26 | - id: prettier 27 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: blog 2 | channels: 3 | - conda-forge 4 | - nodefaults 5 | dependencies: 6 | - ablog 7 | - cftime 8 | - colorspacious 9 | - dask 10 | - dask-labextension 11 | - distributed 12 | - hvplot 13 | - ipywidgets 14 | - ipywidgets 15 | - jupyterlab>=3 16 | - matplotlib 17 | - myst-nb>=0.12.0 18 | - netcdf4 19 | - numba 20 | - pip 21 | - pydantic 22 | - pydata-sphinx-theme>=0.4.3 23 | - python=3.9 24 | - scipy 25 | - seaborn 26 | - sphinx 27 | - sphinx-autobuild 28 | - sphinx-comments 29 | - sphinx-panels 30 | - sphinxcontrib-bibtex<2.0.0 31 | - sympy 32 | - watermark 33 | - wrapt 34 | - xarray>=0.18.2 35 | - pooch 36 | - pip: 37 | - ptime 38 | - sphinx-design 39 | - sphinxext-opengraph 40 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | live: 16 | sphinx-autobuild --ignore */.github/* --ignore */tmp/* --ignore */drafts/* "$(SOURCEDIR)" _build/dirhtml/ --port 9999 17 | 18 | .PHONY: help Makefile 19 | 20 | # Catch-all target: route all unknown targets to Sphinx using the new 21 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 22 | %: Makefile 23 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 24 | -------------------------------------------------------------------------------- /_templates/hello.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |
Anderson Banihirwe
7 |
8 | Interactive High Performance Computing | Open Source Science 9 |
10 | 11 |
12 | I contribute to and maintain several libraries within the open source 13 | scientific Python stack, particularly around improving scalability of Python 14 | tools in order to handle terabyte-scale datasets on HPC and cloud platforms. 15 |
16 | 17 | 18 | 23 | 24 |
25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/andersy005/blog/linting/main?logo=github) 2 | [![pre-commit.ci status](https://results.pre-commit.ci/badge/github/andersy005/blog/main.svg)](https://results.pre-commit.ci/latest/github/andersy005/blog/main) 3 | 4 | # [blog.andersonbanihirwe.dev](https://blog.andersonbanihirwe.dev/) 5 | 6 | This repository serves my personal website, technical blog, and a collection of my personal notes. The contents are posted at [blog.andersonbanihirwe.dev](https://blog.andersonbanihirwe.dev/). 7 | 8 | ## License 9 | 10 | Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. 11 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: deploy-site 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | workflow_dispatch: 11 | 12 | jobs: 13 | deploy-site: 14 | runs-on: ubuntu-latest 15 | defaults: 16 | run: 17 | shell: bash -l {0} 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - name: set up conda environment 22 | uses: mamba-org/setup-micromamba@v1 23 | with: 24 | environment-file: environment.yml 25 | init-shell: >- 26 | bash 27 | cache-environment: true 28 | cache-downloads: true 29 | post-cleanup: "all" 30 | 31 | - name: Build 32 | run: | 33 | make dirhtml 34 | 35 | - name: Deploy 36 | if: github.ref == 'refs/heads/main' 37 | uses: peaceiris/actions-gh-pages@v4.0.0 38 | with: 39 | github_token: ${{ secrets.GITHUB_TOKEN }} 40 | publish_dir: ./_build/dirhtml 41 | cname: blog.andersonbanihirwe.dev 42 | enable_jekyll: false 43 | -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | # Anderson Banihirwe 2 | 3 | Currently, I am working as a software engineer in [Computational & Information Systems Lab (CISL)](https://www2.cisl.ucar.edu/) at the [National Center for Atmospheric Research (NCAR)](https://ncar.ucar.edu/). On the interwebs, I go by [@andersy005](https://github.com/andersy005). 4 | 5 | I’ve been extremely fortunate to work in realms that allow me to cross institutional and geographic boundaries to collaborate on tools and research software. More generally I contribute to and maintain several libraries within the open source scientific Python stack, particularly around improving scalability of Python tools in order to (1) handle large scale datasets on [High Performance Computing](https://en.wikipedia.org/wiki/Supercomputer) and [Cloud Computing](https://en.wikipedia.org/wiki/Cloud_computing) platforms and (2) move the [**open science paradigm**](https://en.wikipedia.org/wiki/Open_science) forward. 6 | 7 | ## Recent Posts 8 | 9 | See the [blog archives](posts) for a more complete list. 10 | 11 | ```{postlist} 12 | :date: "%Y-%m-%d" 13 | :format: "{date} - {title}" 14 | :excerpts: 15 | ``` 16 | 17 | ```{toctree} 18 | :maxdepth: 2 19 | :hidden: 20 | posts 21 | about 22 | talks 23 | teaching 24 | 🧾 CV 25 | Video 26 | ``` 27 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.ruff] 2 | line-length = 100 3 | target-version = "py312" 4 | extend-include = ["*.ipynb"] 5 | 6 | builtins = ["ellipsis"] 7 | # Exclude a variety of commonly ignored directories. 8 | exclude = [ 9 | ".bzr", 10 | ".direnv", 11 | ".eggs", 12 | ".git", 13 | ".git-rewrite", 14 | ".hg", 15 | ".ipynb_checkpoints", 16 | ".mypy_cache", 17 | ".nox", 18 | ".pants.d", 19 | ".pyenv", 20 | ".pytest_cache", 21 | ".pytype", 22 | ".ruff_cache", 23 | ".svn", 24 | ".tox", 25 | ".venv", 26 | ".vscode", 27 | "__pypackages__", 28 | "_build", 29 | "buck-out", 30 | "build", 31 | "dist", 32 | "node_modules", 33 | "site-packages", 34 | "venv", 35 | ".pixi" 36 | ] 37 | per-file-ignores = {} 38 | ignore = [ 39 | "E721", # Comparing types instead of isinstance 40 | "E741", # Ambiguous variable names 41 | "E501", # Conflicts with ruff format 42 | ] 43 | select = [ 44 | # Pyflakes 45 | "F", 46 | # Pycodestyle 47 | "E", 48 | "W", 49 | # isort 50 | "I", 51 | # Pyupgrade 52 | "UP", 53 | ] 54 | 55 | 56 | 57 | [tool.ruff.lint.mccabe] 58 | max-complexity = 18 59 | 60 | [tool.ruff.lint.isort] 61 | known-first-party = [] 62 | combine-as-imports = true 63 | 64 | [tool.ruff.format] 65 | quote-style = "single" 66 | docstring-code-format = true 67 | 68 | [tool.ruff.lint.pydocstyle] 69 | convention = "numpy" 70 | -------------------------------------------------------------------------------- /posts/2020/maurice-hilleman.md: -------------------------------------------------------------------------------- 1 | --- 2 | author: Anderson Banihirwe 3 | date: 2020-12-16 4 | tags: life, science, people, podcast 5 | --- 6 | 7 | # On Maurice Hilleman 8 | 9 | I spent today's evening listening to [_The Great Vaccinator_](https://www.wnycstudios.org/podcasts/radiolab/articles/great_vaccinator) episode from Radiolab podcast. This episode is about [Maurice Hilleman](https://en.wikipedia.org/wiki/Maurice_Hilleman), an amazing microbiologist who is known today as the father of modern vaccine. Hilleman is credited with having saved more lives than any other scientist because he was responsible for developing more than half of the vaccines (measles, mumps, meningitis, etc...) children receive today. 10 | 11 | 12 | 13 | His life was marked by life-changing losses. He was born in the middle of the 1918 flu pandemic, and his mother and twin sister passed away right after his birth. As a young boy, he survived a couple of traumatic situations (e.g. drowning). He lost his wife from his first marriage to cancer. Amazingly, he stayed a pretty resilient, hard-working person throughout it all. I was enamored by his modesty and humility (which are part of the reason why today most people don't even know who he is). I mean... the guy developed a lot of vaccines but none of his vaccines or discoveries are named after him. 14 | 15 | _This episode was extremely satisfying to listen to and it was a great episode for better understanding our times._ 16 | -------------------------------------------------------------------------------- /posts/2021/freedom-of-cryptography.md: -------------------------------------------------------------------------------- 1 | --- 2 | author: Anderson Banihirwe 3 | date: 2021-02-21 4 | tags: todayilearned, cryptography 5 | --- 6 | 7 | # Freedom of Cryptography 8 | 9 | Today I learned that if you could travel back to the early 1990s with the contents of this [git repository](https://github.com/pyca/cryptography), you would have had to become a licensed arms dealer 😮 —the govt certainly would have denied you an export license— before you could post the source code (or any work that featured the source code) on the internet. 10 | 11 | Publicly sharing the following code snippet would have landed you in prison for arms trafficking because export of cryptography from the United States was regulated at the same level as missiles, tanks, etc...😮. 12 | 13 | ```python 14 | In [1]: from cryptography.fernet import Fernet 15 | 16 | In [2]: key = Fernet.generate_key() 17 | 18 | In [3]: f = Fernet(key) 19 | 20 | In [4]: plain_text = b'Hello Alice' 21 | 22 | In [5]: cipher_text = f.encrypt(plain_text) 23 | 24 | In [6]: cipher_text 25 | Out[6]: b'gAAAAABgMjSru96WoOLHq3ygBw93ZICzyu5aKjYQqw0MQ7G44Ch77-xqCVNmYSbB4j6Kz_Ipol3hyPWx5Ti5rui1SdRc_D7LfA==' 26 | 27 | In [7]: f.decrypt(cipher_text) 28 | Out[7]: b'Hello Alice' 29 | ``` 30 | 31 | **Resources with more deailed history of the legal battles for freedom of cryptography**: 32 | 33 | - [Bernstein v. United States](https://en.wikipedia.org/wiki/Bernstein_v._United_States) 34 | - [Crypto: How the Code Rebels Beat the Government Saving Privacy in the Digital Age](https://www.amazon.com/Crypto-Rebels-Government-Privacy-Digital/dp/0140244328) 35 | - [The Case against Regulating 36 | Encryption Technology](https://people.csail.mit.edu/rivest/pubs/Riv98e.pdf) 37 | -------------------------------------------------------------------------------- /_static/custom.css: -------------------------------------------------------------------------------- 1 | /* Bio area */ 2 | div.profile-pic { 3 | margin-top: 1em; 4 | } 5 | 6 | div.profile-pic img { 7 | border-radius: 500px; 8 | width: 80%; 9 | max-width: 190px; 10 | margin: 0 auto; 11 | display: block; 12 | } 13 | 14 | .bio-info { 15 | margin: 1em auto; 16 | max-width: 220px; 17 | } 18 | 19 | .name { 20 | font-size: 1.5em; 21 | } 22 | 23 | .focusareas { 24 | font-size: 0.9em; 25 | font-weight: bold; 26 | } 27 | 28 | .whatido { 29 | margin-top: 1em; 30 | } 31 | 32 | /* Sidebar for blog archive / each post */ 33 | ul.ablog-archive { 34 | padding-left: 0px; 35 | } 36 | 37 | .bd-sidebar h2 { 38 | font-size: 1.4em; 39 | } 40 | 41 | .bd-sidebar ul { 42 | padding-left: 0; 43 | list-style-type: none; 44 | } 45 | 46 | .bd-sidebar li { 47 | padding-bottom: 0.5em; 48 | } 49 | 50 | div.bd-sidebar h3, 51 | div.bd-sidebar h2, 52 | div.bd-sidebar ul { 53 | padding-left: 5%; 54 | } 55 | 56 | /* In-page post lists */ 57 | ul.postlist { 58 | padding-left: 0; 59 | } 60 | 61 | ul.postlist > li > p:first-child { 62 | font-size: 1.5em; 63 | } 64 | 65 | ul.postlist li + li { 66 | margin-top: 2em; 67 | } 68 | 69 | ul.postlist li > p > a { 70 | font-style: normal; 71 | font-size: 1.3em; 72 | } 73 | 74 | /* Timeline CSS */ 75 | div.timeline div.card { 76 | border: 0px; 77 | } 78 | 79 | div.timeline div.left { 80 | text-align: right; 81 | border-right: 1px solid black; 82 | } 83 | 84 | div.timeline div.entry::after { 85 | width: 1em; 86 | height: 1em; 87 | background: white; 88 | border-radius: 50%; 89 | content: ""; 90 | top: 1em; 91 | display: block; 92 | position: absolute; 93 | border: 1px black solid; 94 | z-index: 999; 95 | } 96 | 97 | div.timeline div.entry.left::after { 98 | right: -0.5em; 99 | } 100 | 101 | div.timeline div.entry.right::after { 102 | left: -0.5em; 103 | } 104 | -------------------------------------------------------------------------------- /config_data/teaching.yaml: -------------------------------------------------------------------------------- 1 | - name: NCAR Python Tutorial Seminar Series - Xarray Part 1 2 | video: '' 3 | date: 2021-06-09 4 | repository: https://github.com/andersy005/xarray-tutorial 5 | - name: NCAR Python Tutorial Seminar Series - Xarray Part 2 6 | video: '' 7 | date: 2021-06-23 8 | repository: https://github.com/andersy005/xarray-tutorial 9 | - name: NCAR Python Tutorial Seminar Series - Dask Part 1 10 | video: '' 11 | date: 2021-07-29 12 | repository: https://github.com/andersy005/xarray-tutorial 13 | - name: NCAR Python Tutorial Seminar Series - Dask P2 14 | video: '' 15 | date: 2021-08-11 16 | repository: https://github.com/andersy005/xarray-tutorial 17 | - name: Xarray Tutorial | xarray fundamentals 18 | video: '' 19 | date: 2020-10-06 20 | repository: https://github.com/andersy005/xarray-tutorial 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # PyCharm project settings 98 | .idea/ 99 | 100 | # Rope project settings 101 | .ropeproject 102 | 103 | # mkdocs documentation 104 | /site 105 | 106 | # mypy 107 | .mypy_cache/ 108 | 109 | 110 | # Sphinx 111 | _build/ 112 | .vscode/ 113 | pip-wheel-metadata/* 114 | .DS_Store 115 | dask-worker-space/ 116 | posts/2020/2020-12-03-advent-of-code-day-3-Copy1.ipynb 117 | .gitignore 118 | posts/2020/2020-12-xx-advent-of-code-day-xx-Copy1.ipynb 119 | .pixi 120 | -------------------------------------------------------------------------------- /data/advent-of-code/2020/day-1-input: -------------------------------------------------------------------------------- 1 | 1895 2 | 1602 3 | 1592 4 | 1714 5 | 1403 6 | 1766 7 | 1654 8 | 1771 9 | 1957 10 | 1533 11 | 1661 12 | 1761 13 | 1711 14 | 1869 15 | 1541 16 | 1595 17 | 1819 18 | 1735 19 | 1894 20 | 1316 21 | 1777 22 | 1450 23 | 1526 24 | 1888 25 | 1968 26 | 1877 27 | 1972 28 | 1988 29 | 1655 30 | 1369 31 | 1636 32 | 1453 33 | 1969 34 | 1239 35 | 1717 36 | 1444 37 | 1907 38 | 1682 39 | 1358 40 | 1706 41 | 1482 42 | 1852 43 | 1689 44 | 1905 45 | 1262 46 | 1956 47 | 770 48 | 1507 49 | 1314 50 | 1890 51 | 784 52 | 1710 53 | 1418 54 | 597 55 | 1417 56 | 1587 57 | 2003 58 | 1889 59 | 879 60 | 1534 61 | 279 62 | 1302 63 | 1976 64 | 1936 65 | 1590 66 | 1939 67 | 1489 68 | 1966 69 | 1238 70 | 1481 71 | 1412 72 | 1675 73 | 2001 74 | 1543 75 | 1220 76 | 1352 77 | 1536 78 | 1879 79 | 1892 80 | 2006 81 | 1642 82 | 1321 83 | 1600 84 | 1908 85 | 2009 86 | 1784 87 | 1260 88 | 1881 89 | 1897 90 | 1933 91 | 1048 92 | 1851 93 | 2005 94 | 1626 95 | 1441 96 | 1945 97 | 1742 98 | 1734 99 | 1816 100 | 1919 101 | 1802 102 | 1460 103 | 1845 104 | 1914 105 | 1652 106 | 1943 107 | 1521 108 | 1830 109 | 1477 110 | 1866 111 | 1702 112 | 1629 113 | 1932 114 | 1671 115 | 1707 116 | 1577 117 | 1962 118 | 1518 119 | 1989 120 | 1502 121 | 61 122 | 1546 123 | 1264 124 | 1651 125 | 2000 126 | 1443 127 | 1931 128 | 1882 129 | 1583 130 | 1597 131 | 1487 132 | 1255 133 | 1779 134 | 1782 135 | 1540 136 | 1580 137 | 1294 138 | 1691 139 | 1337 140 | 1743 141 | 1632 142 | 1348 143 | 2010 144 | 1794 145 | 1876 146 | 1808 147 | 1647 148 | 422 149 | 1994 150 | 1864 151 | 1996 152 | 1738 153 | 1998 154 | 1749 155 | 1789 156 | 1395 157 | 1997 158 | 1440 159 | 1676 160 | 1527 161 | 1523 162 | 1836 163 | 1366 164 | 1700 165 | 1826 166 | 1426 167 | 1709 168 | 166 169 | 1958 170 | 1909 171 | 1428 172 | 1984 173 | 521 174 | 1760 175 | 156 176 | 1296 177 | 1475 178 | 1566 179 | 1573 180 | 1696 181 | 1471 182 | 1788 183 | 1809 184 | 1942 185 | 1461 186 | 1559 187 | 1699 188 | 1504 189 | 1465 190 | 1658 191 | 1973 192 | 1679 193 | 1376 194 | 1985 195 | 1503 196 | 1517 197 | 1825 198 | 1847 199 | 1528 200 | 1246 201 | -------------------------------------------------------------------------------- /posts/2020/the-sun-and-her-scorch.md: -------------------------------------------------------------------------------- 1 | --- 2 | author: Anderson Banihirwe 3 | date: 2020-08-02 4 | tags: music 5 | --- 6 | 7 | # Dizzy - The Sun and Her Scorch: You don’t just hear it, you feel it 8 | 9 | Two years ago I stumble upon [indie music](https://en.wikipedia.org/wiki/Independent_music), and have never looked back since then. Today, I love indie music for how true to the soul it is. One of my favorite indie artists is [**Dizzy**](https://www.dizzytheband.com/), a Canadian indie alt-pop four-piece band. Dizzy recently released their new record _The Sun and Her Scorch_. It is so beautiful! My top two songs on this record are: 10 | 11 | - **The Magician**: which is about Katie Munshaw (who is the vocalist of the band) wanting to magically bring a friend who passed away back to real life. 12 | 13 | > You be the bird, I'll be a magician 🎶 14 | 15 | 16 | 17 | Personally, this song reminds me of a great college friend of mine who passed away in a tragic accident in 2019. Overall, the song sounds like such an emotional story portrayed beautifully with a little hope. 18 | 19 | - **Roman Candles**: which is about the spiraling anxiety and fear one feels whenever they think of having made the wrong career choice. 20 | 21 | > “The song is about being nervous and scared that I've made the wrong decision in pursuing music as a career. I feel like I'm being left behind in a way. I think it's hard for people to understand music as a career when you're not famous. If you're not Dua Lipa, people are worried about you. They don't realise that it is a career option. It can be hard to relate to people, especially when I'm not going to work every morning. Christmas dinners are usually pretty awkward. 'Oh, you're still doing music, eh?' 'Yes, grandpa!'” -- Katie Munshaw, [Apple Music](https://music.apple.com/nz/album/the-sun-and-her-scorch/1508164390) 22 | 23 | 24 | 25 | On a personal note, I’ve been extremely lucky to (1) have a fulfilling job that I enjoy (2) to work in realms that allow me to cross institutional and geographic boundaries to collaborate on tools and research software. I'm so thankful for the opportunity to help with moving science forward. 26 | 27 | _In [a year (2020)](https://en.wikipedia.org/wiki/2020) that in the future may be known as “the year we shall not speak of”, Music has been a great source of solace...._ 28 | 29 | Here's the full album on spotify. Enjoy 😊 30 | 31 | 32 | -------------------------------------------------------------------------------- /posts/2020/chadwick-boseman.md: -------------------------------------------------------------------------------- 1 | --- 2 | author: Anderson Banihirwe 3 | date: 2020-08-28 4 | tags: life, people 5 | --- 6 | 7 | # Chadwick Boseman: Rest Easy King T’Challa. Wakanda 🙅🏾‍♂️ forever 8 | 9 | Chadwick Boseman: 10 | 11 | - **Thank you for teaching and showing us how to serve others, while quietly understanding their pain.** 12 | 13 | 14 | 15 | - **Thank you for being the light to so many people, sharing your beautiful spirit and talent with the world! We will miss you so .... ✊🏾 ❤️ 🙅🏾‍♂️ forever and ever ...** 16 | 17 | 18 | 19 | - **Throughout it all you found the courage and the will to persevere while uplifting those around you. You didn't just play a hero, you were truly one!** 20 | 21 | 22 | 23 | 24 | 25 | Repose en paix, Chadwick Boseman. Tu nous manques déjà... 26 | 27 | ![black-panther](../../_static/images/black-panther.jpeg) 28 | 29 | > “He who has gone, so we but cherish his memory, abides with us, more potent, nay, more present than the living man.” — Antoine de Saint-Exupery 30 | -------------------------------------------------------------------------------- /about.md: -------------------------------------------------------------------------------- 1 | # About me 2 | 3 | I work as a software engineer in the [Computational & Information Systems Lab (CISL)](https://www2.cisl.ucar.edu/) at the [National Center for Atmospheric Research (NCAR)](https://ncar.ucar.edu/). 4 | 5 | ## Software 6 | 7 | As part of my work at the National Center for Atmospheric Research, I am a core contributor to many projects in the PyData ecosystem. A complete record of projects that I have contributed to is available on my [{fa}`github,style=fab` GitHub page](https://github.com/andersy005). 8 | 9 | ## Publications 10 | 11 | For a list of publications in which I've been involved, 12 | check out [my ORCID page](https://orcid.org/0000-0001-6583-571X) or [my Google Scholar page](https://scholar.google.com/citations?user=vrlgltgAAAAJ&hl=en&oi=ao). 13 | 14 | ## Academic and Work timeline 15 | 16 | Below is a timeline of what I've been up to over the past several years. 17 | 18 | ```{panels} 19 | :container: timeline 20 | :column: col-6 p-0 21 | :card: 22 | 23 | --- 24 | :column: +entry left 25 | 26 | **Sept 2020**: Promoted to Software Engineer || 27 | ^^^ 28 | 29 | I continued leading the intake-ESM project. In addition, I joined the [Jupyter Meets Earth](https://pangeo-data.github.io/jupyter-earth/) project as a contributor. During this time, I helped with the development of the following projects: 30 | - [jupyter-forward](https://jupyter-forward.readthedocs.io/), a Jupyter Lab port forwarding utility that simplifies running jupyter on remote resources. 31 | 32 | --- 33 | :column: +right 34 | --- 35 | :column: +left 36 | 37 | --- 38 | :column: +entry right 39 | 40 | **Oct 2018**: Joined NCAR as Software Engineer | 41 | ^^^ 42 | 43 | After my work at Quansight, I joined NCAR as a fulltime Software Engineer I focusing my work around developing Python tools for climate data analysis. Here are a few of the main things I did during this time: 44 | 45 | - Lead the [intake-ESM](https://intake-esm.readthedocs.io/en/latest/) project, a Python data cataloguing package for exploring and ingesting earth system model data sets. 46 | - Contributed to the core software stack powering the [Pangeo Project](https://pangeo.io/). Some of the projects I contributed to include: 47 | - [xarray](https://xarray.pydata.org/en/stable/) 48 | - [dask](https://docs.dask.org/en/latest/) 49 | - [distributed](https://distributed.dask.org/en/latest/) 50 | - [dask-mpi](https://mpi.dask.org/en/latest/) 51 | - [dask-jobqueue](https://jobqueue.dask.org/en/latest/) 52 | 53 | --- 54 | :column: +entry left 55 | 56 | **June 2018**: Joined Quansight 57 | ^^^ 58 | 59 | After graduating I joined [Quansight](https://www.quansight.com/) as a software developer intern. At Quansight, I developed [xndframes](https://github.com/xnd-project/xndframes), an experimental Pandas extension array backed by xnd. In addition to xndframes, I worked on [cuDF](https://github.com/rapidsai/cudf) a GPU dataframe library. 60 | 61 | --- 62 | :column: +right 63 | --- 64 | :column: +left 65 | --- 66 | :column: +entry right 67 | 68 | **May 2018**: Graduated from UA-Little Rock 69 | ^^^ 70 | 71 | Graduated from [University of Arkansas at Little Rock](http://ualr.edu/systemsengineering/) with a B.S. in Systems Engineering. 72 | ``` 73 | -------------------------------------------------------------------------------- /posts/2021/mesdames.md: -------------------------------------------------------------------------------- 1 | --- 2 | author: Anderson Banihirwe 3 | date: 2021-03-13 4 | tags: music 5 | --- 6 | 7 | # « Mesdames » - Parce que l’avenir appartient à celles qu’on aime trop 🎶 8 | 9 | Ça fait des années que je n’ai pas parlé français ou que j’ai écrit en français. 10 | Mon excuse numéro un, c'est que je réside dans un pays anglophone. Et à cause de ça, je n’ai pas l’occasion d’utiliser (parler, écrir) la langue régulièrement. Pourtant, j'ai plein d'occasions de savourer: 11 | 12 | - des programmes diffusés à la télé ([Quotidien avec Yann Barthès](https://www.tf1.fr/tmc/quotidien-avec-yann-barthes) est mon numéro un préféré 🤩). 13 | - beaucoup de chansons (merci Spotify, Youtube :)) 14 | 15 | Ce blog post est ma première tentative de blogger en français. Je vais essayer d'écrire en français chaque fois que l' occasion se présentera. Dans ce blog post, je veux parler du nouvel album de [Grand Corps Malade](https://en.wikipedia.org/wiki/Grand_Corps_Malade) baptisé **["Mesdames"]()**. Je viens d'ecouter à l'intégralité de cet album. L'album est formidable et il est avant tout un hommage aux femmes. Voici mes morceaux préférés: 16 | 17 | - **Une sœur**: _Il y a peu de monde qui te permette de donner au mot famille un vrai sens_ ... 🎶 18 | 19 | 20 | 21 | - **Mesdames**: _Vous êtes infiniment plus subtiles, plus élégantes et plus classes_ ...🎶 22 | 23 | 24 | 25 | - **Enfants du désordre**: _La violence est une rancœur qu'on a laissé grandir 26 | Une colère prisonnière qui ne veut plus partir_ ... 🎶 27 | 28 | 29 | 30 | - **Mais je t'aime**: _Ces flammes nous ont rendus fous 31 | On a oublié qu'au final, le feu ça brûle.... Pourquoi lorsque l'amour est fort il nous rend vulnérables et fragiles_...🎶 32 | 33 | 34 | 35 | **Cet album est un vrai chef-d'œuvre! Musique, voix, textes, etc... tout est magnifique!** Pour les amoureux de la musique, voici l'album complet sur Spotify. Enjoy 😊... 36 | 37 | 38 | 39 | ## Bonus 40 | 41 | - Pour une dose de bonne humeur, voici **"Mais j'te tej"**, une parodie trop drôle de **"Mais je t'aime"**. Je me suis bien marré 😂😂😂 42 | 43 | 44 | -------------------------------------------------------------------------------- /talks_gallery.md: -------------------------------------------------------------------------------- 1 | ```{panels} 2 | :card: text-center 3 | 4 | [Community Earth System Model Large Ensemble (CESM LENS) Datasets on AWS](https://talks.andersonbanihirwe.dev/presentations/2022/esip-jan-cesm-lens-aws.html) 5 | ^^^ 6 | ... 7 | +++ 8 | ESIP January 2022 Meeting | Virtual | 2022-01-19 9 | 10 | --- 11 | 12 | [The Current State of Deploying Dask on HPC systems](https://talks.andersonbanihirwe.dev/deploying-dask-on-hpc-dask-summit-2021.html) 13 | ^^^ 14 | 15 | +++ 16 | Dask Summit 2021 | Virtual | 2021-05-20 17 | 18 | --- 19 | 20 | [Pangeo Use Case: Analyzing Initialized Climate Prediction System Datasets with climpred](https://talks.andersonbanihirwe.dev/climpred-cdpw-2020.html) 21 | ^^^ 22 | ... 23 | +++ 24 | NOAA’s 45th Climate Diagnostics & Prediction Workshop | Virtual | 2020-10-20 25 | 26 | --- 27 | 28 | [Zarr: chunked, compressed, multidimensional arrays](https://talks.andersonbanihirwe.dev/zarr-cloud-native-geospatial-2020.html) 29 | ^^^ 30 | 31 | +++ 32 | Cloud Native Geospatial Outreach Day 2020 | Virtual | 2020-09-08 33 | 34 | --- 35 | 36 | [Intake-ESM – Making It Easier To Consume Climate and Weather Data](https://talks.andersonbanihirwe.dev/intake-esm-esip-2020.html) 37 | ^^^ 38 | 39 | +++ 40 | ESIP Summer Meeting 2020 | Virtual | 2020-07-23 41 | 42 | --- 43 | 44 | [Dask and Pangeo](https://speakerdeck.com/andersy005/dask-and-pangeo-a-community-platform-for-big-data-geoscience) 45 | ^^^ 46 | ... 47 | +++ 48 | Dask Developer Summit 2020 | Washington, District of Columbia, U.S. | 2020-02-26 49 | 50 | --- 51 | 52 | [Turning HPC Systems into Interactive Data Analysis Platforms using Jupyter and Dask](https://talks.andersonbanihirwe.dev/dask-jupyter-scipy-2019.html) 53 | ^^^ 54 | 55 | +++ 56 | SciPy Conference 2019 | Austin, Texas, U.S. | 2019-07-12 57 | 58 | --- 59 | 60 | [PySpark for "Big" Atmospheric Data Analysis](https://opensky.ucar.edu/islandora/object/conference%3A3443/datastream/PDF/view) 61 | ^^^ 62 | 63 | +++ 64 | Eighth Symposium on Advances in Modeling and Analysis Using Python. American Meteorological Society | Austin, Texas, U.S. | 2018-01-08 65 | 66 | --- 67 | 68 | [PySpark for "Big" Atmospheric and Oceanic Data Analysis](...) 69 | ^^^ 70 | 71 | +++ 72 | 2017 NCAR/CISL SIParCS Internship | Boulder, Colorado, U.S. | 2017-08-23 73 | 74 | ``` 75 | -------------------------------------------------------------------------------- /posts/2021/lets-encrypt-wildcard-ssl-certificate-on-centos.md: -------------------------------------------------------------------------------- 1 | --- 2 | author: Anderson Banihirwe 3 | date: 2021-02-27 4 | tags: todayilearned, dns, domains, letsencrypt 5 | --- 6 | 7 | # How to Generate Wildcard SSL Certificate using Let’s Encrypt/Certbot on CentOS 8 | 9 | Today I wanted to generate a wildcard SSL certificate for a service I was working on. Luckily, Let's Encrypt's ACME v2 production endpoint makes it so easy to generate wilcard certificates (for more details on this feature, see [this post](https://community.letsencrypt.org/t/acme-v2-production-environment-wildcards/55578)). Below are the steps I used to generate a wildcard certificate. 10 | 11 | :::{note} 12 | This article describes the process for Centos 7/8 but can also be used for other Linux distros (with small changes in step 1). Also, `{subdomain}` and `{domain}` are placeholders. Remember to replace them with your own values. For instance, `{subdomain}`-> `mycloud`, and `{domain}` -> `example.com` 13 | ::: 14 | 15 | ## Step 1 — Installing the Certbot Let’s Encrypt Client 16 | 17 | ```bash 18 | $ sudo dfn install epel-release 19 | $ sudo dfn install certbot 20 | ``` 21 | 22 | :::{note} 23 | 24 | This step assumes that you are using CentOS 7/8. 25 | 26 | ::: 27 | 28 | ## Step 2 — Obtaining a Wildcard Certificate 29 | 30 | ```bash 31 | $ sudo certbot certonly -d *.{subdomain}.{domain} \ 32 | > --manual \ 33 | > --preferred-challenges dns \ 34 | > --server https://acme-v02.api.letsencrypt.org/directory 35 | ``` 36 | 37 | The certbot prompts you for some personal information. Once everything is done, certbot provides a DNS TXT record to add to your DNS settings. At this point, head over to your DNS settings ( I was using google domains) and enter your TXT DNS record: 38 | 39 | ![google-domains-acme-challenge](../../_static/images/google-domains-acme-challenge.png) 40 | 41 | :::{note} 42 | 43 | `{subdomain}` and `{domain}` are placeholders. Remember to replace them with your own values. For instance, `{subdomain}`-> `mycloud`, and `{domain}` -> `example.com` 44 | 45 | ::: 46 | 47 | At this point, 48 | 49 | - Go back to your command line command (which should have been blocking after the DNS TXT record instructions). 50 | - Then Hit `Enter`. 51 | 52 | Cerbot will retrieve your DNS records to verify that the entries are correct. If the verification is successful, you get following output: 53 | 54 | ```console 55 | Before continuing, verify the record is deployed. 56 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 57 | Press Enter to Continue 58 | Waiting for verification... 59 | Cleaning up challenges 60 | 61 | IMPORTANT NOTES: 62 | - Congratulations! Your certificate and chain have been saved at: 63 | /etc/letsencrypt/live/mycloud.example.com/fullchain.pem 64 | Your key file has been saved at: 65 | /etc/letsencrypt/live/mycloud.example.com/privkey.pem 66 | Your certificate will expire on 2021-05-28. To obtain a new or 67 | tweaked version of this certificate in the future, simply run 68 | certbot again. To non-interactively renew *all* of your 69 | certificates, run "certbot renew" 70 | - If you like Certbot, please consider supporting our work by: 71 | 72 | Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate 73 | Donating to EFF: https://eff.org/donate-le 74 | ``` 75 | 76 | ## Step 3 — Testing the Certificate and SSL Configuration 77 | 78 | At this point, you can ensure that Certbot created your SSL certificate correctly by using the [SSL Server Test](https://www.ssllabs.com/ssltest/) from the cloud security company Qualys. 79 | 80 | Open your browser, and point it to the following link, replacing `mycloud.example.com` with your domain: 81 | 82 | ```console 83 | https://www.ssllabs.com/ssltest/analyze.html?d=mycloud.example.com 84 | ``` 85 | 86 | Voilà 🎉🎉🎉🎉! 87 | -------------------------------------------------------------------------------- /posts/2021/google-dynamic-dns-wildcard-subdomains.md: -------------------------------------------------------------------------------- 1 | --- 2 | author: Anderson Banihirwe 3 | date: 2021-01-09 4 | tags: todayilearned, dns, domains 5 | --- 6 | 7 | # How to set up a wildcard subdomain on Google Domains 8 | 9 | Today, I was working with [dokku](https://github.com/dokku/dokku/), and I found myself in need of a wildcard subdomain. A [wildcard DNS record](https://en.wikipedia.org/wiki/Wildcard_DNS_record) is a record that will match requests for non-existent subdomains. For instance, if you set a wildcard DNS record to `*.example.com`, and a user requests `random.example.com` or `foo.example.com`, etc.. the requests will be forwarded to whichever server `*.example.com` points to. 10 | 11 | For my use case, I wanted to host multiple applications on a single server, and wanted to map each app to a subdomain such as `app1-name.dokku.example.com`, `app2-name.dokku.example.com`, etc. To achieve this, I needed to set up a wildcard DNS record for `*.dokku.example.com`. 12 | 13 | Currently, I happen to own a domain on [Google Domains](https://domains.google/). Below are the steps I followed to set up the `*.dokku.example.com` wildcard DNS record. 14 | 15 | :::{note} 16 | I'm using `example.com` as a dummy domain for demonstration purposes. 17 | ::: 18 | 19 | ## Server/VM with a dynamic IP address 20 | 21 | For a server with a dynamic IP address, we need to create a dynamic DNS record and a CNAME record in our DNS settings. 22 | 23 | ### Step 1: Create a Dynamic DNS record in Synthetic Records 24 | 25 | Create a synthetic record for **dokku.example.com** instead of **\*.dokku.example.com**: 26 | 27 | ![](../../_static/images/google-domains-dynamic-dns.png) 28 | 29 | ### Step 2: Create a CNAME record of the subdomain 30 | 31 | Create a CNAME record of the subdomain **\*dokku.example.com** and point it to the synthetic records subdomain **dokku.example.com**: 32 | 33 | ![](../../_static/images/google-domains-wildcard-subdomain.png) 34 | 35 | ### Step 3: Set up ddclient on your server 36 | 37 | First, make sure [`ddclient`](https://github.com/ddclient/ddclient) is installed on your server and the `ddclient` service is up and running. 38 | 39 | Second, 40 | 41 | ```bash 42 | $ sudo nano /etc/ddclient.conf 43 | ``` 44 | 45 | And edit these lines using the credentials from the created Google Domains dynamic DNS record: 46 | 47 | ```bash 48 | ## 49 | ## Google Domains (www.google.com/domains) 50 | ## 51 | # protocol=googledomains, 52 | # login=my-auto-generated-username, 53 | # password=my-auto-generated-password 54 | # my.domain.tld, otherhost.domain.tld 55 | ``` 56 | 57 | - Example: 58 | 59 | ```bash 60 | daemon=3600 # Check every 1 hour 61 | ssl=yes 62 | use=web, web=checkip.dyndns.com/, web-skip='IP Address' 63 | protocol=googledomains 64 | login=xxzzzxxxxxxzzzzzz 65 | password=xxzzzzxxxxzzzzz 66 | dokku.example.com 67 | ``` 68 | 69 | Verify that the `ddclient` is working by trying: 70 | 71 | ```bash 72 | sudo ddclient -daemon=0 -debug -verbose -noquiet 73 | ``` 74 | 75 | ### Step 4: Verify that our changes have taken effect 76 | 77 | We can verify our changes on a UNIX machine by trying one or more of the following commands: 78 | 79 | - `host dokku.example.com` 80 | - `dig dokku.example.com` 81 | - `nslookup dokku.example.com` 82 | 83 | Once we have a confirmation that our top level subdomain works, we should be able to query any other name under `dokku.example.com` and get back an IP address of our server. Try: 84 | 85 | - `host myapp.dokku.example.com` 86 | - `dig +short '*.dokku.example.com'` 87 | 88 | Credits: Thank you [StackExchange](https://serverfault.com/questions/670066/google-dynamic-dns-wildcard-subdomains) 🙏🏽! 89 | 90 | ## Server/VM with a static IP address 91 | 92 | For a server/VM with a static IP address, the process is much simpler. Creating an `A record` pointing to the IP address of the server/VM suffices: 93 | 94 | ![](../../_static/images/google-domains-static-ip.png) 95 | -------------------------------------------------------------------------------- /config_data/talks.yaml: -------------------------------------------------------------------------------- 1 | - title: "Community Earth System Model Large Ensemble (CESM LENS) Datasets on AWS" 2 | slides: https://talks.andersonbanihirwe.dev/presentations/2022/esip-jan-cesm-lens-aws.html 3 | video: ... 4 | conference: 5 | name: ESIP January 2022 Meeting 6 | location: Virtual 7 | date: 2022-01-19 8 | 9 | - title: "The Current State of Deploying Dask on HPC systems" 10 | slides: https://talks.andersonbanihirwe.dev/deploying-dask-on-hpc-dask-summit-2021.html 11 | video: '' 12 | conference: 13 | name: Dask Summit 2021 14 | location: Virtual 15 | date: 2021-05-20 16 | 17 | - title: "Pangeo Use Case: Analyzing Initialized Climate Prediction System Datasets with climpred" 18 | slides: https://talks.andersonbanihirwe.dev/climpred-cdpw-2020.html 19 | video: ... 20 | conference: 21 | name: NOAA’s 45th Climate Diagnostics & Prediction Workshop 22 | location: Virtual 23 | date: 2020-10-20 24 | 25 | - title: "Zarr: chunked, compressed, multidimensional arrays" 26 | slides: https://talks.andersonbanihirwe.dev/zarr-cloud-native-geospatial-2020.html 27 | video: '' 28 | conference: 29 | name: Cloud Native Geospatial Outreach Day 2020 30 | location: Virtual 31 | date: 2020-09-08 32 | 33 | - title: "Intake-ESM – Making It Easier To Consume Climate and Weather Data" 34 | slides: https://talks.andersonbanihirwe.dev/intake-esm-esip-2020.html 35 | video: '' 36 | conference: 37 | name: ESIP Summer Meeting 2020 38 | location: Virtual 39 | date: 2020-07-23 40 | 41 | - title: "Dask and Pangeo" 42 | slides: https://speakerdeck.com/andersy005/dask-and-pangeo-a-community-platform-for-big-data-geoscience 43 | video: ... 44 | conference: 45 | name: Dask Developer Summit 2020 46 | location: Washington, District of Columbia, U.S. 47 | date: 2020-02-26 48 | 49 | - title: "Turning HPC Systems into Interactive Data Analysis Platforms using Jupyter and Dask" 50 | slides: https://talks.andersonbanihirwe.dev/dask-jupyter-scipy-2019.html 51 | video: '' 52 | conference: 53 | name: SciPy Conference 2019 54 | location: Austin, Texas, U.S. 55 | date: 2019-07-12 56 | 57 | - title: 'PySpark for "Big" Atmospheric Data Analysis' 58 | slides: https://opensky.ucar.edu/islandora/object/conference%3A3443/datastream/PDF/view 59 | video: '' 60 | conference: 61 | name: Eighth Symposium on Advances in Modeling and Analysis Using Python. American Meteorological Society 62 | location: Austin, Texas, U.S. 63 | date: 2018-01-08 64 | 65 | - title: 'PySpark for "Big" Atmospheric and Oceanic Data Analysis' 66 | slides: ... 67 | video: '' 68 | conference: 69 | name: 2017 NCAR/CISL SIParCS Internship 70 | location: Boulder, Colorado, U.S. 71 | date: 2017-08-23 72 | -------------------------------------------------------------------------------- /posts/2020/a-knitting-weekend-with-dash.md: -------------------------------------------------------------------------------- 1 | --- 2 | author: Anderson Banihirwe 3 | date: 2020-01-21 4 | tags: dash, documentation, docset, python 5 | --- 6 | 7 | # A Knitting Weekend with Dash and Python 8 | 9 | For the last few months, I've become a huge fan of the **quiet weekend**, the weekend when no plan is the plan, and you are in no hurry at all. I woke up on Saturday morning, and I decided that this weekend was going to be a quiet one. After watching a late morning football game from the English Premier League -- yup, I refuse to call it soccer 😀 -- I decided to write some code-nothing big, just a few lines of codes to improve my experience with [Dash](https://kapeli.com/dash), a tool that I've been using for a few months. 10 | 11 | Dash is a MacOS application that offers offline documentation for hundreds of programming languages, frameworks and libraries. Dash is awesome for offline programming when using spotty WiFi or on long-haul flights. While it is **free as in beer**, it's one of the programs that are worth paying for. 12 | 13 | ## The Problem 14 | 15 | I've been using Dash for quite some time and have been really satisfied with the preset selection of documentation sets. At some point, I started running into scenarios when I was using libraries/packages whose documentations aren't available for Dash. So, my objective was to generate documentation sets for these libraries myself. I should point out that I mostly work with Python packages, and most Python packages use [sphinx](https://www.sphinx-doc.org/) as their main framework for building documentation. So, my goal was to find a way to easily build documentation sets and integrate them with Dash. 16 | 17 | ## The Solution: Doc2dash and Sphinx To The Rescue 18 | 19 | After few minutes of google searches, I found doc2dash. [doc2dash](https://doc2dash.readthedocs.io/en/stable/) is a great tool for converting HTML documentation to Dash's format. doc2dash takes the output of Sphinx documentation and converts it to a Dash documentation package. 20 | 21 | ### Show Me The Code! 22 | 23 | In Just a few steps, I was able to build a docset for [Zarr](https://zarr.readthedocs.io/en/stable/) using dash2doc: 24 | 25 | - Create new conda environment and install dependencies 26 | 27 | ```bash 28 | $ conda create -n dash-docs -c conda-forge \ 29 | doc2dash sphinx-rtd-theme numpydoc sphinx-issues 30 | $ conda activate dash-docs 31 | ``` 32 | 33 | - Pull down the project form GitHub and build the documentation 34 | 35 | ```bash 36 | $ git clone https://github.com/zarr-developers/zarr-python.git 37 | $ cd zarr-python 38 | $ python -m pip install -e . 39 | $ cd docs 40 | $ make html 41 | ``` 42 | 43 | - Finally, run doc2dash with a few commands 44 | 45 | ```bash 46 | $ doc2dash --name zarr --index-page index.html --enable-js \ 47 | --add-to-dash _build/html 48 | ``` 49 | 50 | And with that I had a shiny new Zarr docset in Dash: 51 | 52 | ![](../../_static/images/navigate.png) 53 | 54 | Once I was confident that I understood how to do this for one project, I decided to automate this process for a bunch of other projects. By dinner time (on Saturday) I felt like I was on the right track. After a few more hours on Sunday and Monday, I had a [GitHub repository](https://github.com/andersy005/dash-docsets) with continuous integration via CircleCI up and running, and had a dozen docsets generated: 55 | 56 | ![](../../_static/images/dash-docsets-repo.png) 57 | 58 | ![](../../_static/images/docsets.png) 59 | 60 | ## Was It Worth Fifteen or Twenty Hours of My Life? 61 | 62 | **I think so:** I took pleasure in doing something that I knew how to do and in creating something that would fit my needs perfectly. 63 | 64 | Sitting in a chair in front a computer screen while listening to MIX 100 Denver Radio station 😀, I was reminded of the [intriguing parallels between knitting and programming](https://www.nytimes.com/2019/05/17/science/math-physics-knitting-matsumoto.html): 65 | 66 | > "Knitting is Coding, and Yarn is a Programmable material." 67 | 68 | _On quiet weekends like this I write code for fun, and Visual Studio Code & Python are my yarn and needles._ 69 | -------------------------------------------------------------------------------- /teaching_gallery.md: -------------------------------------------------------------------------------- 1 | ``````{grid} 2 2 | :class-container: full-width 3 | 4 | `````{grid-item-card} Xarray Tutorial | xarray fundamentals 5 | :text-align: center 6 | 7 | +++ 8 | 9 | ````{grid} 2 2 2 2 10 | :margin: 0 0 0 0 11 | :padding: 0 0 0 0 12 | :gutter: 1 13 | 14 | ```{grid-item} 15 | :child-direction: row 16 | :child-align: start 17 | :class: sd-fs-5 18 | {bdg-link-secondary}`repo ` 19 | ``` 20 | 21 | ```{grid-item} 22 | :child-direction: row 23 | :child-align: end 24 | [![GitHub Repo stars](https://img.shields.io/github/stars/andersy005/xarray-tutorial?style=social)](https://github.com/andersy005/xarray-tutorial) 25 | ``` 26 | 27 | ```` 28 | 29 | ````` 30 | 31 | `````{grid-item-card} NCAR Python Tutorial Seminar Series - Xarray Part 1 32 | :text-align: center 33 | 34 | +++ 35 | 36 | ````{grid} 2 2 2 2 37 | :margin: 0 0 0 0 38 | :padding: 0 0 0 0 39 | :gutter: 1 40 | 41 | ```{grid-item} 42 | :child-direction: row 43 | :child-align: start 44 | :class: sd-fs-5 45 | {bdg-link-secondary}`repo ` 46 | ``` 47 | 48 | ```{grid-item} 49 | :child-direction: row 50 | :child-align: end 51 | [![GitHub Repo stars](https://img.shields.io/github/stars/andersy005/xarray-tutorial?style=social)](https://github.com/andersy005/xarray-tutorial) 52 | ``` 53 | 54 | ```` 55 | 56 | ````` 57 | 58 | `````{grid-item-card} NCAR Python Tutorial Seminar Series - Xarray Part 2 59 | :text-align: center 60 | 61 | +++ 62 | 63 | ````{grid} 2 2 2 2 64 | :margin: 0 0 0 0 65 | :padding: 0 0 0 0 66 | :gutter: 1 67 | 68 | ```{grid-item} 69 | :child-direction: row 70 | :child-align: start 71 | :class: sd-fs-5 72 | {bdg-link-secondary}`repo ` 73 | ``` 74 | 75 | ```{grid-item} 76 | :child-direction: row 77 | :child-align: end 78 | [![GitHub Repo stars](https://img.shields.io/github/stars/andersy005/xarray-tutorial?style=social)](https://github.com/andersy005/xarray-tutorial) 79 | ``` 80 | 81 | ```` 82 | 83 | ````` 84 | 85 | `````{grid-item-card} NCAR Python Tutorial Seminar Series - Dask Part 1 86 | :text-align: center 87 | 88 | +++ 89 | 90 | ````{grid} 2 2 2 2 91 | :margin: 0 0 0 0 92 | :padding: 0 0 0 0 93 | :gutter: 1 94 | 95 | ```{grid-item} 96 | :child-direction: row 97 | :child-align: start 98 | :class: sd-fs-5 99 | {bdg-link-secondary}`repo ` 100 | ``` 101 | 102 | ```{grid-item} 103 | :child-direction: row 104 | :child-align: end 105 | [![GitHub Repo stars](https://img.shields.io/github/stars/andersy005/xarray-tutorial?style=social)](https://github.com/andersy005/xarray-tutorial) 106 | ``` 107 | 108 | ```` 109 | 110 | ````` 111 | 112 | `````{grid-item-card} NCAR Python Tutorial Seminar Series - Dask P2 113 | :text-align: center 114 | 115 | +++ 116 | 117 | ````{grid} 2 2 2 2 118 | :margin: 0 0 0 0 119 | :padding: 0 0 0 0 120 | :gutter: 1 121 | 122 | ```{grid-item} 123 | :child-direction: row 124 | :child-align: start 125 | :class: sd-fs-5 126 | {bdg-link-secondary}`repo ` 127 | ``` 128 | 129 | ```{grid-item} 130 | :child-direction: row 131 | :child-align: end 132 | [![GitHub Repo stars](https://img.shields.io/github/stars/andersy005/xarray-tutorial?style=social)](https://github.com/andersy005/xarray-tutorial) 133 | ``` 134 | 135 | ```` 136 | 137 | ````` 138 | 139 | `````` 140 | -------------------------------------------------------------------------------- /posts/2021/how-to-merge-disk-partitions-on-centos.md: -------------------------------------------------------------------------------- 1 | --- 2 | author: Anderson Banihirwe 3 | date: 2021-01-07 4 | tags: todayilearned, linux, centos 5 | --- 6 | 7 | # How to merge two or more disk partitions on Centos 7 8 | 9 | I've been working with centos 7 virtual machine provisioned via VMware's vrealize suite. One thing I particulary dislike is how the storage disk gets partitioned into tiny partititions during the VM provisioning: 10 | 11 | ```bash 12 | $ df -h 13 | Filesystem Size Used Avail Use% Mounted on 14 | devtmpfs 1.9G 0 1.9G 0% /dev 15 | tmpfs 1.9G 0 1.9G 0% /dev/shm 16 | tmpfs 1.9G 8.9M 1.9G 1% /run 17 | tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup 18 | /dev/mapper/centos_dhcp--zzz--zzz--zzz--zz-root 6.0G 70M 6.0G 2% / 19 | /dev/mapper/centos_dhcp--zzz--zzz--zzz--zz-usr 6.0G 1.5G 4.6G 24% /usr 20 | /dev/sda1 1014M 232M 783M 23% /boot 21 | /dev/mapper/centos_dhcp--zzz--zzz--zzz--zz-data 61G 34M 61G 1% /data 22 | /dev/mapper/centos_dhcp--zzz--zzz--zzz--zz-usr_local 6.0G 33M 6.0G 1% /usr/local 23 | /dev/mapper/centos_dhcp--zzz--zzz--zzz--zz-home 10G 33M 10G 1% /home 24 | /dev/mapper/centos_dhcp--zzz--zzz--zzz--zz-var 6.0G 414M 5.6G 7% /var 25 | /dev/mapper/centos_dhcp--zzz--zzz--zzz--zz-tmp 2.0G 33M 2.0G 2% /tmp 26 | tmpfs 379M 0 379M 0% /run/user/1001 27 | ``` 28 | 29 | Notice how `var`, `data`, `home`, `tmp`, `usr`, `usr_local`, and `root` have their own partitions. I prefer to have a few but large disk partitions. So, today I figured out how to merge two or more partitions into the root partition (thank you, @kmpaul for the help and documenting this!). 30 | 31 | ## Step 1 — Make sure you are logged in as root 32 | 33 | ## Step 2 — Backup data from the partitions you want to merge into the root partiton 34 | 35 | ```bash 36 | $ rsync -a /home/ /home-old/ 37 | $ rsync -a /tmp/ /tmp-old/ 38 | $ rsync -a /var/ /var-old/ 39 | ``` 40 | 41 | ## Step 3 — Reboot the VM into an emergency mode 42 | 43 | ```bash 44 | $ systemctl emergency 45 | ``` 46 | 47 | ## Step 4 — Umount and remove logic volume for each of the partitions 48 | 49 | ```bash 50 | $ umount /dev/mapper/centos_dhcp--zzz--zzz--zzz--zz-data 51 | $ umount /dev/mapper/centos_dhcp--zzz--zzz--zzz--zz-home 52 | $ umount /dev/mapper/centos_dhcp--zzz--zzz--zzz--zz-var 53 | $ umount /dev/mapper/centos_dhcp--zzz--zzz--zzz--zz-tmp 54 | ``` 55 | 56 | ```bash 57 | $ lvremove /dev/mapper/centos_dhcp--zzz--zzz--zzz--zz-data 58 | $ lvremove /dev/mapper/centos_dhcp--zzz--zzz--zzz--zz-home 59 | $ lvremove /dev/mapper/centos_dhcp--zzz--zzz--zzz--zz-var 60 | $ lvremove /dev/mapper/centos_dhcp--zzz--zzz--zzz--zz-tmp 61 | ``` 62 | 63 | ## Step 5 — Copy the backed up data 64 | 65 | ```bash 66 | $ rsync -a /home-old/ /home/ 67 | $ rsync -a /var-old/ /var/ 68 | $ rsync -a /tmp-old/ /tmp/ 69 | ``` 70 | 71 | ## Step 6 — Edit the `/etc/fstab` file by removing or commenting out the partitions we don't need 72 | 73 | ```bash 74 | $ vi /etc/fstab 75 | ``` 76 | 77 | ## Step 7 — Extend the root partition to fill the remaining space 78 | 79 | ```bash 80 | $ lvextend -l +100%FREE -r /dev/mapper/centos_dhcp--zzz--zzz--zzz--zz-root 81 | ``` 82 | 83 | ## Step 8 — Remove the backups 84 | 85 | ```bash 86 | $ rm -rf /home-old/ /tmp-old/ /var-old/ 87 | ``` 88 | 89 | ## Step 9 — Reboot the system 90 | 91 | ```bash 92 | $ reboot 93 | ``` 94 | 95 | ## Step 10 — Login to the VM as a regular user or root 96 | 97 | Let's check that our `/` root partition size has increased: 98 | 99 | ```bash 100 | $ df -h 101 | Filesystem Size Used Avail Use% Mounted on 102 | devtmpfs 1.9G 0 1.9G 0% /dev 103 | tmpfs 1.9G 0 1.9G 0% /dev/shm 104 | tmpfs 1.9G 8.9M 1.9G 1% /run 105 | tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup 106 | /dev/mapper/centos_dhcp--zzz--zzz--zzz--zz-root 85G 474M 85G 1% / 107 | /dev/mapper/centos_dhcp--zzz--zzz--zzz--zz-usr 6.0G 1.5G 4.6G 24% /usr 108 | /dev/sda1 1014M 232M 783M 23% /boot 109 | /dev/mapper/centos_dhcp--zzz--zzz--zzz--zz-usr_local 6.0G 33M 6.0G 1% /usr/local 110 | tmpfs 379M 0 379M 0% /run/user/1001 111 | ``` 112 | 113 | Voilà! 🙌 114 | -------------------------------------------------------------------------------- /posts/2022/multitude.md: -------------------------------------------------------------------------------- 1 | --- 2 | author: Anderson Banihirwe 3 | date: 2022-03-06 4 | tags: music, francais 5 | --- 6 | 7 | # « Multitude de Stromae » - Des morceaux aussi forts sur des sujets si primordials 🎶 8 | 9 | J'ai passé le week-end en ecoutant en boucle l'intégralité du nouvel album de Stromae, intitulé [**"Multitude"**](https://music.youtube.com/playlist?list=OLAK5uy_kDes3UKBSh3zb1ZwRE8r3x6-6bDTM6jzY). L'album est sublime. Les morceaux, les instrumentals sont sensationnels. Les rythmes, les thèmes abordés (la dépression, la solitude, le suicide, la maladie, la prostitution, le féminisme, l'abandon, etc...) sont bien équilibrés 👏🏽🙏🏽! 10 | 11 | ![multitude-cover](https://upload.wikimedia.org/wikipedia/en/6/67/Stromae_Multitude_cover.jpg) 12 | _© Stromae – Multitude / Universal Music_ 13 | 14 | Dans ce blog, je veux partager mes morceaux préférés de cet album. 15 | 16 | - **Invaincu**: est un morceau puissant sur la survie, alimenté par des beats triomphants 🔥🔥: 17 | 18 | > « Tant que je suis en vie je suis invaincu » ... 19 | 20 | 21 | 22 | - **La solassitude**: l'instrumental est juste phenoménal. Stromae présente l'ironie de la vie et de l'amour (la solitude quand on est célibataire, la lassitude en couple): 23 | 24 | > « Le problème, c'est la routine, quand les jours se ressеmblent... C'qui m'tue, c'est l'еnnui, est-c'qu'on finira ensemble? » ... 25 | 26 | > « Le célibat me fait souffrir de solitude, La vie de couple me fait souffrir de lassitude » ... 27 | 28 | 29 | 30 | - **Riez**: Sûrement le meilleur morceau (numero 1 de mon classement). Le sujet de la chanson est les rêves, les fantasmes de la vie. La chanson se comprend à l'envers: de vouloir la nécessité fondamentale de la vie à tout avoir et pourtant pas satisfait et en vouloir toujours plus. Rions pour le reveur en chacun de nous: 31 | 32 | > « C’que j’voudrais c’est d’manger à ma faim, 33 | > Tous les jours de l’année.. 34 | > Et puis j’me demande si enfin, 35 | > En grand, j’pourrais rêver » ... 36 | 37 | > « Moi un jour, je s’rai un grand artiste, 38 | > J’gagnerai même un Grammy.. 39 | > J’aurai des sous et tellement je s’rai riche, 40 | > J’aurai même plein d’amis » ... 41 | 42 | 43 | 44 | - **Déclaration**: Un délice ce morceau (numero 2 de mon classement). Dans ce sublime, sobre chanson avec un message clair, Stromae plaidoye pour les femmes ✨🎗. "Déclaration" aborde des sujets sociaux qui reposent entièrement sur les épaules des femmes: enfanter, la contraception et l’endométriose: 45 | 46 | > « Si l’courage avait un visage, il aurait l’tien » ... 47 | 48 | > « Toujours obligée d’aimer enfanter, La contraception qui te détruit la santé, Endométriose, enchantée, J'suis mieux payé que toi sans vouloir me vanter » ... 49 | 50 | > « T’inquiète pas ça va aller, faudra bien que ça change. Ça prendra quelques années vu que ça nous arrange » ... 51 | 52 | 53 | 54 | - **Bonne journée**: le morceaux plaisir coupable: 55 | 56 | > « Le bonheur est bien la seule chose qui quand on la partage se multiplie 57 | > Je vois la vie en rose, dis moi, est ce que tu la vois aussi ? » ... 58 | 59 | > « Si l'bonheur des autres te rends malheureux, c'est que t'es un rageux 60 | > Si l'malheur des autres te rends heureux hmm, c'est que t'es un rageux » ... 61 | 62 | 63 | 64 | Comme le titre l'indique, l'album est vraiment une multitude de sons, une multitude de themes, instruments, etc... **37 minutes de pure qualité** pour les amoureux de la musique et surtout pour les fans de Stromae. L'album est vraiment très propre après plusieurs écoute. Il valait la peine d'attendre :). Voici l'album complet sur YouTube. Enjoy 😊... 65 | 66 | 67 | -------------------------------------------------------------------------------- /posts/drafts/bayesian-for-hackers/probability-distributions.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import ipywidgets\n", 10 | "import numpy as np\n", 11 | "import proplot as plt\n", 12 | "import scipy.stats" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": 2, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "a = np.arange(16)\n", 22 | "colors = ['#348ABD', '#A60628']" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 3, 28 | "metadata": {}, 29 | "outputs": [], 30 | "source": [ 31 | "def poisson_dist(λ=1.5):\n", 32 | " fig, ax = plt.subplots(aspect=(3.5, 2), width=6)\n", 33 | " ax.bar(\n", 34 | " a,\n", 35 | " scipy.stats.poisson.pmf(a, λ),\n", 36 | " color=colors[0],\n", 37 | " alpha=0.60,\n", 38 | " edgecolor=colors[0],\n", 39 | " lw='3',\n", 40 | " label=rf'$\\lambda ={λ}',\n", 41 | " )\n", 42 | " ax.format(\n", 43 | " xticks=a,\n", 44 | " ylabel='Probability of $k$',\n", 45 | " xlabel='$k$',\n", 46 | " title='Probability mass function of a Poisson random variable',\n", 47 | " )\n", 48 | " plt.close(fig)\n", 49 | " return fig" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 4, 55 | "metadata": {}, 56 | "outputs": [ 57 | { 58 | "data": { 59 | "application/vnd.jupyter.widget-view+json": { 60 | "model_id": "5065558a0b5f4e82a19fa8b6c041e6fd", 61 | "version_major": 2, 62 | "version_minor": 0 63 | }, 64 | "text/plain": [ 65 | "interactive(children=(FloatSlider(value=1.5, description='λ', max=20.0, min=1.0, step=1.0), Output()), _dom_cl…" 66 | ] 67 | }, 68 | "metadata": {}, 69 | "output_type": "display_data" 70 | } 71 | ], 72 | "source": [ 73 | "ipywidgets.interact(poisson_dist, λ=ipywidgets.FloatSlider(min=1, max=20, value=1.5, step=1))" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": 5, 79 | "metadata": {}, 80 | "outputs": [], 81 | "source": [ 82 | "a = np.linspace(0, 4, 100)\n", 83 | "\n", 84 | "\n", 85 | "def exponential_dist(λ=0.5):\n", 86 | " exp = scipy.stats.expon.pdf(a, scale=1.0 / λ)\n", 87 | " fig, ax = plt.subplots(aspect=(3.5, 2), width=6)\n", 88 | " ax.plot(a, exp, color=colors[0], label=rf'$\\lambda ={λ}', lw=3)\n", 89 | " ax.fill_between(a, exp, color=colors[0], alpha=0.33)\n", 90 | " ax.format(\n", 91 | " ylabel='Probability density function at of $z$',\n", 92 | " xlabel='$z$',\n", 93 | " title='Probability density function of an exponential random variable',\n", 94 | " ylim=(0, 1.2),\n", 95 | " )\n", 96 | " plt.close(fig)\n", 97 | " return fig" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": 6, 103 | "metadata": {}, 104 | "outputs": [ 105 | { 106 | "data": { 107 | "application/vnd.jupyter.widget-view+json": { 108 | "model_id": "63237957561241fda8edec586f6b4b07", 109 | "version_major": 2, 110 | "version_minor": 0 111 | }, 112 | "text/plain": [ 113 | "interactive(children=(FloatSlider(value=0.5, description='λ', max=1.0, min=0.1), Output()), _dom_classes=('wid…" 114 | ] 115 | }, 116 | "metadata": {}, 117 | "output_type": "display_data" 118 | } 119 | ], 120 | "source": [ 121 | "ipywidgets.interact(exponential_dist, λ=ipywidgets.FloatSlider(min=0.1, max=1, value=0.5))" 122 | ] 123 | }, 124 | { 125 | "cell_type": "code", 126 | "execution_count": 7, 127 | "metadata": {}, 128 | "outputs": [ 129 | { 130 | "name": "stdout", 131 | "output_type": "stream", 132 | "text": [ 133 | "Last updated: 2020-11-27\n", 134 | "\n", 135 | "Git hash: 0f38d2abc8e59398ce2708ab976146a134652ccf\n", 136 | "\n", 137 | "Git repo: git@github.com:andersy005/blog.git\n", 138 | "\n", 139 | "Git branch: main\n", 140 | "\n", 141 | "scipy : 1.5.3\n", 142 | "ipywidgets: 7.5.1\n", 143 | "numpy : 1.19.4\n", 144 | "proplot : 0.6.4\n", 145 | "\n" 146 | ] 147 | } 148 | ], 149 | "source": [ 150 | "%load_ext watermark\n", 151 | "%watermark -grb -du -iv" 152 | ] 153 | } 154 | ], 155 | "metadata": { 156 | "kernelspec": { 157 | "display_name": "Python 3", 158 | "language": "python", 159 | "name": "python3" 160 | }, 161 | "language_info": { 162 | "codemirror_mode": { 163 | "name": "ipython", 164 | "version": 3 165 | }, 166 | "file_extension": ".py", 167 | "mimetype": "text/x-python", 168 | "name": "python", 169 | "nbconvert_exporter": "python", 170 | "pygments_lexer": "ipython3", 171 | "version": "3.8.5" 172 | }, 173 | "widgets": { 174 | "application/vnd.jupyter.widget-state+json": { 175 | "state": {}, 176 | "version_major": 2, 177 | "version_minor": 0 178 | } 179 | } 180 | }, 181 | "nbformat": 4, 182 | "nbformat_minor": 4 183 | } 184 | -------------------------------------------------------------------------------- /posts/2021/dictionary-merge-and-update-operators.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "difficult-gather", 6 | "metadata": {}, 7 | "source": [ 8 | "# Dictionary Merge and Update Operators in Python 3.9\n", 9 | "\n", 10 | "When it comes to merging two dictinaries in Python, I find myself googling \"how to merge two dictionaries\" very often. Every time I land on this [StackOverflow answer](https://stackoverflow.com/questions/38987/how-do-i-merge-two-dictionaries-in-a-single-expression-taking-union-of-dictiona). This answers recommends using the **dictionary unpacking** feature:" 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": 1, 16 | "id": "705af7b6-2a9c-48b8-997e-0085065f2506", 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "x = {'earth': 5.97, 'mercury': 0.330}\n", 21 | "y = {'uranus': 86.8, 'mars': 0.642, 'earth': -999}" 22 | ] 23 | }, 24 | { 25 | "cell_type": "code", 26 | "execution_count": 2, 27 | "id": "e1b0c166-f93f-4b98-812f-1b53d5f19c4c", 28 | "metadata": {}, 29 | "outputs": [ 30 | { 31 | "data": { 32 | "text/plain": [ 33 | "{'earth': -999, 'mercury': 0.33, 'uranus': 86.8, 'mars': 0.642}" 34 | ] 35 | }, 36 | "execution_count": 2, 37 | "metadata": {}, 38 | "output_type": "execute_result" 39 | } 40 | ], 41 | "source": [ 42 | "a = {**x, **y}\n", 43 | "a" 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "id": "610a134b-99c6-40a6-b840-61f14e1ab183", 49 | "metadata": {}, 50 | "source": [ 51 | "Even though I have seen dictionary unpacking used for unpacking keyword arguments in functions, I've **rarely seen it being used as a method for merging dictionaries**, and this makes it a **bit confusing** if you haven't seen it used in this context. I was delighted when I found out that Python 3.9 introduced a dedicated binary operator that permits merging two dictionaries. In Python 3.9, we can use the `|` (bitwise `OR`) to perform a dictionary merge:" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": 3, 57 | "id": "c8ec51c4-9d85-4acd-b95a-ff9fe39c5142", 58 | "metadata": {}, 59 | "outputs": [ 60 | { 61 | "data": { 62 | "text/plain": [ 63 | "{'earth': -999, 'mercury': 0.33, 'uranus': 86.8, 'mars': 0.642}" 64 | ] 65 | }, 66 | "execution_count": 3, 67 | "metadata": {}, 68 | "output_type": "execute_result" 69 | } 70 | ], 71 | "source": [ 72 | "b = x | y\n", 73 | "b" 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "id": "bccd2266-d5dc-4c7b-aa47-166022184e32", 79 | "metadata": {}, 80 | "source": [ 81 | "As you can see, the resulting dictionary consists of a new dictionary with all the keys of both `x` and `y` dictionaries. Because `x` and `y` have overlapping key `earth`, the new dictionary uses the values from the rightmost dictionary (i.e. `y`)" 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "id": "830c0a36-e825-4753-9845-e3a4aa0e47f6", 87 | "metadata": {}, 88 | "source": [ 89 | "If we prefer to update one of the dictionary inplace (i.e. without needing to create a new dictionary), we can use the `|=` (in-place bitwise `OR`) operator" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": 4, 95 | "id": "daaf995f-d201-4945-b9a7-e4c17b582054", 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "x |= y" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": 5, 105 | "id": "fab77b58-cc3d-4ea1-83bd-f6752a993447", 106 | "metadata": {}, 107 | "outputs": [ 108 | { 109 | "data": { 110 | "text/plain": [ 111 | "{'earth': -999, 'mercury': 0.33, 'uranus': 86.8, 'mars': 0.642}" 112 | ] 113 | }, 114 | "execution_count": 5, 115 | "metadata": {}, 116 | "output_type": "execute_result" 117 | } 118 | ], 119 | "source": [ 120 | "x" 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "id": "727bce58-99fc-45e9-bf14-ca0f7a30b55d", 126 | "metadata": {}, 127 | "source": [ 128 | "I really dig this new `|` operator. I find the syntax easy to remember and understand compared with the **dictionary unpacking** feature that I used prior to Python 3.9. There's one problem though. This operator is only available for Python 3.9+ and I have to write code for older versions of Python for the time being 😞." 129 | ] 130 | } 131 | ], 132 | "metadata": { 133 | "author": "Anderson Banihirwe", 134 | "date": "2021-06-04", 135 | "kernelspec": { 136 | "display_name": "Python 3", 137 | "language": "python", 138 | "name": "python3" 139 | }, 140 | "language_info": { 141 | "codemirror_mode": { 142 | "name": "ipython", 143 | "version": 3 144 | }, 145 | "file_extension": ".py", 146 | "mimetype": "text/x-python", 147 | "name": "python", 148 | "nbconvert_exporter": "python", 149 | "pygments_lexer": "ipython3", 150 | "version": "3.9.4" 151 | }, 152 | "tags": "python,todayilearned", 153 | "title": "Dictionary Merge and Update Operators in Python 3.9", 154 | "widgets": { 155 | "application/vnd.jupyter.widget-state+json": { 156 | "state": {}, 157 | "version_major": 2, 158 | "version_minor": 0 159 | } 160 | } 161 | }, 162 | "nbformat": 4, 163 | "nbformat_minor": 5 164 | } 165 | -------------------------------------------------------------------------------- /posts/2019/scipy-2019.md: -------------------------------------------------------------------------------- 1 | --- 2 | author: Anderson Banihirwe 3 | date: 2019-07-21 4 | tags: scipy, conference 5 | --- 6 | 7 | # Reflecting on SciPy 2019: My first SciPy, definitely not my last 8 | 9 | It's been a week since SciPy 2019 conference came to a close. I had the pleasure of speaking at SciPy 2019 about [**interactive supercomputing with Dask and Jupyter**](https://andersonbanihirwe.dev/talks/dask-jupyter-scipy-2019.html). I am so thankful for the chance to speak in the Earth, Ocean, Geo and Atmospheric Science track! Here's a recording of my talk: 10 | 11 | 12 | 13 | As I look back at my first [SciPy](https://www.scipy2019.scipy.org/), I can say that it was the most productive conference that I've been to so far. So, I wanted to take this opportunity to summarize my key takeaways. 14 | 15 | ## 1. The Awesomeness of SciPy Community 16 | 17 | The biggest highlight of SciPy for me was the **community**. 18 | **What an authentic, super-talented, enthusiastic and welcoming community!** I met and interacted with a lot of amazing people that I only knew via Github prior the conference :). The community is really what makes SciPy such a special conference. 19 | 20 | ## 2. Invisible Work in Open Source Software Projects 21 | 22 | > Because so much of the work in OSS is tracked on public platforms, it is easy to forget about the work that takes place outside of them. 23 | 24 | This quote by Stuart really hit home. For the last few months, I've become a core maintainer of a few projects, and one of the things I've learned so far as a user, a contributor to, and core maintainer of an open source project is that it's really important to recognize that the github repository does not fully represent a project. **There's quite a lot of crucial, untracked work that goes on in the background** such as: 25 | 26 | ![Credit: Karthik Ram: http://inundata.org/talks/sdss](../../_static/images/hidden-oss-work.png) 27 | 28 | > Credit: Karthik Ram: http://inundata.org/talks/sdss 29 | 30 | You can watch the full key note here: 31 | 32 | 33 | 34 | ## 3. Tell People When You Appreciate Their Work 35 | 36 | Another key lessons I learned is from [Carol Willing's keynote](https://www.youtube.com/watch?v=s-W-UvGgDco&list=PLYx7XA2nY5GcDQblpQ_M1V3PQPoLWiDAC&index=71) is that research shows that meeting someone who has befitted from your work can double your effort and triple your productivity: 37 | 38 | ![Credit: Carol Willing: https://speakerdeck.com/willingc/jupyter-always-open-for-learning-and-discovery](../../_static/images/extract-hidden-value.png) 39 | 40 | > Credit: Carol Willing: https://speakerdeck.com/willingc/jupyter-always-open-for-learning-and-discovery 41 | 42 | ## 4. We Should Think About Open Source Standards 43 | 44 | Matthew Rocklin gave a great talk on [refactoring the SciPy ecosystem for heterogeneous computing](http://matthewrocklin.com/slides/scipy-2019.html). 45 | 46 | 47 | 48 | My takeaway from his talk is that 49 | 50 | > The Python scientific community fractures when we develop competing packages that are not compatible with each other and thrives when we develop tools based on common standards. 51 | 52 | As software developers, we should try to follow already existing standards when we can, because standards level the playing field in so many ways: 53 | 54 | - new technologies can quickly compete 55 | - new developers can quickly engage 56 | - incentivize support (e.g. GitHub renders `.ipynb` since Jupyter notebooks are built on a widely used standard) 57 | 58 | ## 5. Jupyter Notebooks Come Alive with Jupyter Widgets 59 | 60 | One of my favorite SciPy 2019 talks is Martin and Maarten's talk on **Dashboarding with Jupyter Notebooks, Voilà and Widgets**. It is really fun and full of cool stuffs. 61 | 62 | 63 | 64 | I am looking forward to making my own widgets and dashboards! And exclaiming **voilà!** after I deploy them! 65 | 66 | ## 6. Maslow’s Hierarchy of Software Needs 67 | 68 | Stan's talk on **How to Accelerate an Existing Codebase with Numba** is also among my favorites. The Key lesson from this talk is the following: 69 | 70 | > Be honest with yourself about where your project is in this hierarchy. If your program doesn't run, there's no point in making it faster. 71 | 72 | ![Credit: Stan Seibert](../../_static/images/maslow.png) 73 | 74 | > Credit: Stan Seibert 75 | 76 | 77 | 78 | ## 7. Final Thoughts 79 | 80 | I left SciPy 2019 feeling: 81 | 82 | - **very thankful** that so many amazing people are able to come out and collaborate on the projects that are so vital to the Python scientific ecocystem! 83 | - **grateful** to be part of this community. 84 | - **inspired** to keep contributing to this community. 85 | - **so excited** about the technology, the science, and the people. 86 | 87 | **Thank you so much to the SciPy Conference organizers for putting on a great conference.** 88 | -------------------------------------------------------------------------------- /posts/2021/function-expressions-in-js.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Function Expressions in JavaScript\n", 8 | "\n", 9 | "I'm currently learning JavaScript, and here are a few things I learned about\n", 10 | "function expressions in JavaScript ;)\n" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "## Anonymous vs Named Functions\n" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": 1, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "// An anonymous function ==> lambda function in Python\n", 27 | "const clickHandler = function(){\n", 28 | " console.log('The user clicked this button.')\n", 29 | "}" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": 2, 35 | "metadata": {}, 36 | "outputs": [], 37 | "source": [ 38 | "// Named Function \n", 39 | "const keyHandler = function keyHandler() {\n", 40 | " console.log('This is a named function.')\n", 41 | "}" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": 3, 47 | "metadata": {}, 48 | "outputs": [ 49 | { 50 | "name": "stdout", 51 | "output_type": "stream", 52 | "text": [ 53 | "The user clicked this button.\n" 54 | ] 55 | } 56 | ], 57 | "source": [ 58 | "clickHandler()" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 4, 64 | "metadata": {}, 65 | "outputs": [ 66 | { 67 | "name": "stdout", 68 | "output_type": "stream", 69 | "text": [ 70 | "This is a named function.\n" 71 | ] 72 | } 73 | ], 74 | "source": [ 75 | "keyHandler()" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": 5, 81 | "metadata": {}, 82 | "outputs": [], 83 | "source": [ 84 | "const cars = [{make: 'toyota', id: 'Rav4'}, {make: 'tesla', id: 'S'}]" 85 | ] 86 | }, 87 | { 88 | "cell_type": "code", 89 | "execution_count": 6, 90 | "metadata": {}, 91 | "outputs": [ 92 | { 93 | "name": "stdout", 94 | "output_type": "stream", 95 | "text": [ 96 | "[ \u001b[32m'Rav4'\u001b[39m, \u001b[32m'S'\u001b[39m ]\n" 97 | ] 98 | } 99 | ], 100 | "source": [ 101 | "// Arrow Function \n", 102 | "let ids = cars.map(car => car.id)\n", 103 | "console.log(ids)" 104 | ] 105 | }, 106 | { 107 | "cell_type": "code", 108 | "execution_count": 7, 109 | "metadata": {}, 110 | "outputs": [ 111 | { 112 | "name": "stdout", 113 | "output_type": "stream", 114 | "text": [ 115 | "[ \u001b[32m'Rav4'\u001b[39m, \u001b[32m'S'\u001b[39m ]\n" 116 | ] 117 | } 118 | ], 119 | "source": [ 120 | "// An equivalent named function\n", 121 | "ids = cars.map(function getId(car){\n", 122 | " return car.id\n", 123 | "})\n", 124 | "console.log(ids)" 125 | ] 126 | }, 127 | { 128 | "cell_type": "markdown", 129 | "metadata": {}, 130 | "source": [ 131 | "### Takeaways:\n", 132 | "\n", 133 | "- When it comes to\n", 134 | " [Arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions),\n", 135 | " I often find myself having to carefully read the function body to figure out\n", 136 | " what's going on.\n", 137 | "- IMHO, named functions are the best because they tell us in their name what\n", 138 | " they do.\n" 139 | ] 140 | }, 141 | { 142 | "cell_type": "markdown", 143 | "metadata": {}, 144 | "source": [ 145 | "## Immediately Invoked Function Expressions (IIFE)\n" 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": 8, 151 | "metadata": {}, 152 | "outputs": [], 153 | "source": [ 154 | "const school = 'MIT'" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": 9, 160 | "metadata": {}, 161 | "outputs": [ 162 | { 163 | "name": "stdout", 164 | "output_type": "stream", 165 | "text": [ 166 | "CMU\n" 167 | ] 168 | } 169 | ], 170 | "source": [ 171 | "(function anotherSchool(){\n", 172 | " const school = 'CMU'\n", 173 | " console.log(school)\n", 174 | "})()" 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": 10, 180 | "metadata": {}, 181 | "outputs": [ 182 | { 183 | "name": "stdout", 184 | "output_type": "stream", 185 | "text": [ 186 | "MIT\n" 187 | ] 188 | } 189 | ], 190 | "source": [ 191 | "console.log(school)" 192 | ] 193 | }, 194 | { 195 | "cell_type": "markdown", 196 | "metadata": {}, 197 | "source": [ 198 | "### Takeaways:\n", 199 | "\n", 200 | "- IIFEs are typically used in places in our code where we need to collect a set\n", 201 | " of variables and protect them from intruding an outer scope.\n" 202 | ] 203 | } 204 | ], 205 | "metadata": { 206 | "author": "Anderson Banihirwe", 207 | "date": "2021-01-14", 208 | "kernelspec": { 209 | "display_name": "JavaScript", 210 | "language": "javascript", 211 | "name": "jslab" 212 | }, 213 | "language_info": { 214 | "file_extension": ".js", 215 | "mimetype": "text/javascript", 216 | "name": "javascript", 217 | "version": "" 218 | }, 219 | "tags": "javascript,todayilearned", 220 | "title": "Function Expressions in JavaScript", 221 | "widgets": { 222 | "application/vnd.jupyter.widget-state+json": { 223 | "state": {}, 224 | "version_major": 2, 225 | "version_minor": 0 226 | } 227 | } 228 | }, 229 | "nbformat": 4, 230 | "nbformat_minor": 4 231 | } 232 | -------------------------------------------------------------------------------- /conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | import datetime 3 | import pathlib 4 | from textwrap import dedent 5 | from urllib.parse import urlparse 6 | 7 | import yaml 8 | from sphinx.application import Sphinx 9 | from sphinx.util import logging 10 | 11 | LOGGER = logging.getLogger('conf') 12 | 13 | # -- Project information ----------------------------------------------------- 14 | 15 | project = 'Paysage Pythonique' 16 | license_message = ( 17 | 'Except where otherwise noted, this work is licensed under ' 18 | 'a Creative Commons Attribution-ShareAlike 4.0 International License' 19 | ) 20 | copyright = f'2018-{datetime.datetime.now().year}. {license_message}' 21 | author = 'Anderson Banihirwe' 22 | html_last_updated_fmt = '%b %d, %Y' 23 | 24 | extensions = [ 25 | 'myst_nb', 26 | 'ablog', 27 | 'sphinx_panels', 28 | 'sphinx_design', 29 | 'sphinxcontrib.bibtex', 30 | 'sphinxext.opengraph', 31 | 'sphinx_comments', 32 | ] 33 | 34 | # Add any paths that contain templates here, relative to this directory. 35 | templates_path = ['_templates'] 36 | 37 | exclude_patterns = [ 38 | '_build', 39 | 'Thumbs.db', 40 | '.DS_Store', 41 | '*import_posts*', 42 | '**/pandoc_ipynb/inputs/*', 43 | ] 44 | 45 | html_theme = 'pydata_sphinx_theme' 46 | 47 | html_theme_options = { 48 | 'github_url': 'https://github.com/andersy005/', 49 | 'twitter_url': 'https://twitter.com/andersy005', 50 | 'search_bar_text': 'Search this site...', 51 | 'google_analytics_id': 'UA-91185106-1', 52 | 'search_bar_position': 'navbar', 53 | } 54 | html_theme_options['analytics'] = { 55 | # The domain you'd like to use for this analytics instance 56 | 'plausible_analytics_domain': 'blog.andersonbanihirwe.dev', 57 | # The analytics script that is served by Plausible 58 | 'plausible_analytics_url': 'https://plausible.andersonb.xyz/js/script.js', 59 | } 60 | 61 | html_favicon = '_static/share.png' 62 | html_title = '' 63 | 64 | # Add any paths that contain custom static files (such as style sheets) here, 65 | # relative to this directory. They are copied after the builtin static files, 66 | # so a file named "default.css" will overwrite the builtin "default.css". 67 | html_static_path = ['_static'] 68 | html_extra_path = ['feed.xml'] 69 | html_sidebars = { 70 | 'index': ['hello.html'], 71 | 'about': ['hello.html'], 72 | 'publications': ['hello.html'], 73 | 'projects': ['hello.html'], 74 | 'talks': ['hello.html'], 75 | 'posts': ['tagcloud.html', 'archives.html'], 76 | 'posts/**': ['postcard.html', 'recentposts.html', 'archives.html'], 77 | } 78 | blog_baseurl = 'https://blog.andersonbanihirwe.dev' 79 | blog_title = 'Paysage Pythonique' 80 | blog_path = 'posts' 81 | fontawesome_included = True 82 | blog_post_pattern = 'posts/*/*' 83 | post_redirect_refresh = 1 84 | post_auto_image = 1 85 | post_auto_excerpt = 1 86 | 87 | 88 | # Panels config 89 | panels_add_bootstrap_css = False 90 | 91 | # MyST config 92 | myst_enable_extensions = ['amsmath', 'colon_fence', 'deflist', 'html_image'] 93 | myst_url_schemes = ['http', 'https', 'mailto'] 94 | 95 | # OpenGraph config 96 | ogp_site_url = 'https://blog.andersonbanihirwe.dev' 97 | ogp_image = 'https://blog.andersonbanihirwe.dev/_static/share.png' 98 | ogp_site_name = f' {blog_title} | Anderson Banihirwe' 99 | ogp_custom_meta_tags = [ 100 | '', 101 | '', 102 | '', 103 | ] 104 | 105 | # Temporarily stored as off until we fix it 106 | jupyter_execute_notebooks = 'off' 107 | 108 | comments_config = { 109 | 'utterances': {'repo': 'andersy005/blog', 'optional': 'config', 'label': '💬 comment'}, 110 | 'hypothesis': False, 111 | } 112 | 113 | 114 | def build_teaching_gallery(app: Sphinx): 115 | LOGGER.info('Building teaching gallery') 116 | path = pathlib.Path(app.srcdir) / 'config_data/teaching.yaml' 117 | teaching = yaml.safe_load(path.read_text()) 118 | teaching = sorted(teaching, key=lambda item: item['date']) 119 | grid_items = [] 120 | for item in teaching: 121 | if not item.get('video'): 122 | item['video'] = '...' 123 | 124 | repo_text = '' 125 | star_text = '' 126 | 127 | if item['repository']: 128 | repo_text = f'{{bdg-link-secondary}}`repo <{item["repository"]}>`' 129 | 130 | try: 131 | url = urlparse(item['repository']) 132 | if url.netloc == 'github.com': 133 | _, org, repo = url.path.rstrip('/').split('/') 134 | link = f'https://img.shields.io/github/stars/{org}/{repo}?style=social' 135 | star_text = f"[![GitHub Repo stars]({link})]({item['repository']})" 136 | except Exception: 137 | pass 138 | 139 | grid_items.append( 140 | f"""\ 141 | `````{{grid-item-card}} {" ".join(item["name"].split())} 142 | :text-align: center 143 | {item["video"]} 144 | +++ 145 | 146 | ````{{grid}} 2 2 2 2 147 | :margin: 0 0 0 0 148 | :padding: 0 0 0 0 149 | :gutter: 1 150 | 151 | ```{{grid-item}} 152 | :child-direction: row 153 | :child-align: start 154 | :class: sd-fs-5 155 | {repo_text} 156 | ``` 157 | 158 | ```{{grid-item}} 159 | :child-direction: row 160 | :child-align: end 161 | {star_text} 162 | ``` 163 | 164 | ```` 165 | 166 | ````` 167 | """ 168 | ) 169 | grid_items = '\n'.join(grid_items) 170 | 171 | panels = f"""``````{{grid}} 2 172 | :class-container: full-width 173 | 174 | {dedent(grid_items)} 175 | `````` 176 | """ 177 | (pathlib.Path(app.srcdir) / 'teaching_gallery.md').write_text(panels) 178 | 179 | 180 | def build_talks_gallery(app: Sphinx): 181 | LOGGER.info('Building talks gallery') 182 | path = pathlib.Path(app.srcdir) / 'config_data/talks.yaml' 183 | talks = yaml.safe_load(path.read_text()) 184 | talks = sorted(talks, key=lambda item: item['conference']['date'], reverse=True) 185 | LOGGER.info(f'Found {len(talks)} talks') 186 | content = [ 187 | """\ 188 | ```{panels} 189 | :card: text-center 190 | """ 191 | ] 192 | for index, item in enumerate(talks): 193 | content.append( 194 | f"""\ 195 | [{item['title']}]({item['slides']}) 196 | ^^^ 197 | {item['video']} 198 | +++ 199 | {item['conference']['name']} | {item['conference']['location']} | {item['conference']['date']} 200 | """ 201 | ) 202 | 203 | if index < len(talks) - 1: 204 | content.append( 205 | """\ 206 | --- 207 | """ 208 | ) 209 | 210 | content.append( 211 | """\ 212 | ```""" 213 | ) 214 | 215 | content = dedent('\n'.join(content)) 216 | out_path = pathlib.Path(app.srcdir) / 'talks_gallery.md' 217 | out_path.write_text(content) 218 | 219 | 220 | def setup(app: Sphinx): 221 | app.add_css_file('custom.css') 222 | app.connect('builder-inited', build_talks_gallery) 223 | app.connect('builder-inited', build_teaching_gallery) 224 | -------------------------------------------------------------------------------- /data/advent-of-code/2020/day-8-input: -------------------------------------------------------------------------------- 1 | acc -7 2 | acc +2 3 | acc +20 4 | acc +14 5 | jmp +191 6 | acc +47 7 | nop +339 8 | acc +49 9 | jmp +104 10 | jmp +629 11 | jmp +374 12 | acc +24 13 | jmp +220 14 | nop +474 15 | acc +25 16 | jmp +340 17 | acc +16 18 | acc +3 19 | acc +41 20 | jmp +566 21 | jmp +296 22 | acc +15 23 | jmp +452 24 | acc +21 25 | jmp +129 26 | acc +10 27 | acc -8 28 | acc +39 29 | jmp +396 30 | acc +5 31 | acc -4 32 | acc +0 33 | jmp +496 34 | nop +181 35 | acc +48 36 | acc +7 37 | jmp +1 38 | jmp +370 39 | acc +16 40 | acc -18 41 | acc +47 42 | acc +48 43 | jmp +99 44 | nop +17 45 | acc +25 46 | acc -15 47 | jmp +285 48 | nop +545 49 | nop +147 50 | nop +479 51 | acc -4 52 | jmp +386 53 | acc +36 54 | acc -12 55 | jmp +50 56 | acc +37 57 | nop +133 58 | acc +11 59 | acc +20 60 | jmp +32 61 | jmp +1 62 | nop +210 63 | acc -15 64 | acc -6 65 | jmp +446 66 | acc +25 67 | acc +1 68 | acc +17 69 | acc -4 70 | jmp +355 71 | acc -4 72 | jmp +292 73 | acc +16 74 | acc +44 75 | acc +26 76 | jmp +157 77 | acc -18 78 | acc +15 79 | acc -8 80 | acc -3 81 | jmp +46 82 | acc +30 83 | acc +16 84 | jmp -7 85 | acc +34 86 | jmp +515 87 | acc +11 88 | acc -8 89 | acc -9 90 | acc -3 91 | jmp +548 92 | jmp +278 93 | nop +332 94 | acc -19 95 | acc +49 96 | jmp +536 97 | acc -9 98 | acc +46 99 | jmp +124 100 | acc +41 101 | acc +47 102 | acc -5 103 | acc -13 104 | jmp +41 105 | nop +178 106 | acc +12 107 | acc +45 108 | jmp +461 109 | acc +37 110 | acc +12 111 | acc +38 112 | jmp -68 113 | acc -6 114 | nop +494 115 | acc -9 116 | jmp -63 117 | acc +42 118 | acc +16 119 | acc +30 120 | jmp +70 121 | acc +13 122 | jmp +1 123 | acc -18 124 | jmp +528 125 | acc +48 126 | jmp +493 127 | nop +402 128 | jmp +381 129 | acc -8 130 | jmp +372 131 | acc +20 132 | acc +25 133 | jmp +425 134 | acc -10 135 | jmp +510 136 | jmp +439 137 | nop +78 138 | acc +36 139 | acc +7 140 | nop +281 141 | jmp +504 142 | jmp -108 143 | acc +40 144 | jmp -122 145 | acc +23 146 | acc -2 147 | acc +7 148 | jmp +370 149 | acc +25 150 | nop -5 151 | acc +33 152 | acc +37 153 | jmp +70 154 | acc -6 155 | nop +336 156 | jmp +34 157 | jmp +1 158 | acc -18 159 | jmp +473 160 | jmp +1 161 | acc +20 162 | acc +4 163 | acc +25 164 | jmp -87 165 | acc -12 166 | acc +47 167 | acc +49 168 | jmp +323 169 | jmp +1 170 | jmp +1 171 | jmp +167 172 | acc -10 173 | acc +45 174 | jmp +355 175 | acc +32 176 | acc +38 177 | acc +2 178 | jmp -93 179 | acc +8 180 | acc +20 181 | acc +4 182 | acc -1 183 | jmp +108 184 | nop +164 185 | acc +41 186 | jmp +440 187 | acc -16 188 | acc +47 189 | jmp +355 190 | acc -13 191 | acc +29 192 | acc +50 193 | jmp -101 194 | acc -8 195 | jmp +316 196 | acc +27 197 | acc +31 198 | nop -29 199 | jmp +1 200 | jmp +250 201 | acc +12 202 | acc -13 203 | jmp +73 204 | jmp +72 205 | acc +36 206 | acc +44 207 | jmp +1 208 | jmp -33 209 | acc -18 210 | acc +16 211 | acc -8 212 | acc +6 213 | jmp +104 214 | jmp +295 215 | acc +10 216 | nop -80 217 | jmp +74 218 | acc -13 219 | jmp +1 220 | acc +22 221 | acc +50 222 | jmp +280 223 | jmp +265 224 | jmp +278 225 | acc +46 226 | acc -14 227 | acc -17 228 | jmp -19 229 | acc +39 230 | acc +31 231 | acc -11 232 | jmp +400 233 | jmp +80 234 | acc +0 235 | acc +27 236 | nop +209 237 | jmp -184 238 | acc +12 239 | acc +21 240 | acc +23 241 | jmp +352 242 | acc +29 243 | jmp -5 244 | acc +15 245 | acc +7 246 | jmp +6 247 | acc +31 248 | acc -5 249 | nop +83 250 | acc +31 251 | jmp -239 252 | acc +8 253 | acc -2 254 | acc +49 255 | acc -12 256 | jmp -52 257 | acc -15 258 | acc -14 259 | jmp +126 260 | jmp +385 261 | acc +30 262 | acc -5 263 | acc +6 264 | jmp -187 265 | acc +39 266 | acc +40 267 | acc +0 268 | acc +6 269 | jmp +24 270 | acc +20 271 | jmp +131 272 | jmp -127 273 | acc +8 274 | acc +30 275 | jmp -265 276 | acc -2 277 | jmp -265 278 | acc +22 279 | acc -19 280 | acc -9 281 | nop +10 282 | jmp +148 283 | acc -14 284 | acc +38 285 | acc +50 286 | acc -7 287 | jmp +197 288 | acc +11 289 | acc +22 290 | jmp +201 291 | jmp -155 292 | jmp -32 293 | acc +48 294 | nop -50 295 | jmp -99 296 | jmp -5 297 | acc +11 298 | acc -18 299 | jmp -186 300 | acc +6 301 | acc +43 302 | jmp +159 303 | jmp +249 304 | acc +44 305 | acc +29 306 | nop +313 307 | acc +23 308 | jmp +311 309 | jmp +152 310 | acc +0 311 | acc +41 312 | jmp -251 313 | jmp +102 314 | nop -17 315 | nop +176 316 | jmp +40 317 | acc +28 318 | jmp -21 319 | acc -4 320 | acc -10 321 | acc -19 322 | acc -15 323 | jmp +23 324 | nop +144 325 | acc +9 326 | acc +18 327 | jmp +141 328 | acc -19 329 | acc -10 330 | acc +48 331 | jmp -7 332 | acc +46 333 | acc -9 334 | jmp -174 335 | acc +30 336 | acc +30 337 | jmp -201 338 | acc +34 339 | acc +24 340 | acc +37 341 | acc +44 342 | jmp -158 343 | acc +4 344 | acc +39 345 | jmp -52 346 | jmp -329 347 | jmp +68 348 | acc +25 349 | nop -105 350 | acc -15 351 | acc +34 352 | jmp -6 353 | jmp +1 354 | acc +1 355 | jmp +163 356 | nop -285 357 | acc +8 358 | acc +48 359 | jmp +143 360 | acc -3 361 | nop -269 362 | acc -16 363 | jmp -310 364 | acc -5 365 | jmp -304 366 | acc +45 367 | nop -231 368 | jmp +1 369 | jmp +245 370 | nop -243 371 | jmp +187 372 | acc -6 373 | acc +7 374 | acc +17 375 | acc +6 376 | jmp -111 377 | acc +24 378 | acc -10 379 | acc +21 380 | jmp -97 381 | jmp +1 382 | acc -12 383 | acc +10 384 | jmp +127 385 | acc +0 386 | jmp -211 387 | acc -11 388 | acc +36 389 | acc +45 390 | acc -19 391 | jmp -182 392 | jmp -366 393 | acc +38 394 | acc -11 395 | acc +32 396 | jmp -260 397 | acc +6 398 | acc +31 399 | jmp +3 400 | acc +5 401 | jmp +101 402 | jmp -64 403 | acc +48 404 | acc +5 405 | nop +40 406 | acc -13 407 | jmp +95 408 | nop +76 409 | acc +44 410 | acc +43 411 | acc +43 412 | jmp +196 413 | acc +34 414 | jmp +161 415 | acc +5 416 | acc +45 417 | acc +7 418 | jmp +20 419 | acc +13 420 | jmp -127 421 | acc +5 422 | acc +18 423 | jmp -239 424 | jmp -76 425 | nop +214 426 | jmp -284 427 | acc +10 428 | acc -8 429 | jmp -81 430 | acc +48 431 | acc -3 432 | jmp -55 433 | nop -288 434 | acc +37 435 | acc +1 436 | acc -12 437 | jmp +1 438 | nop +91 439 | acc +20 440 | acc +18 441 | jmp +4 442 | acc -7 443 | acc -10 444 | jmp -229 445 | nop -230 446 | nop +45 447 | acc +37 448 | jmp +127 449 | jmp +69 450 | jmp -153 451 | acc -15 452 | acc -19 453 | acc +32 454 | jmp -33 455 | nop +164 456 | acc +32 457 | jmp -133 458 | acc +20 459 | acc -8 460 | jmp +8 461 | acc -11 462 | nop +82 463 | acc +7 464 | acc +40 465 | jmp +79 466 | acc +0 467 | jmp +159 468 | acc +4 469 | acc -8 470 | acc +20 471 | nop +143 472 | jmp -351 473 | acc -7 474 | jmp +78 475 | acc +0 476 | acc +4 477 | jmp +20 478 | jmp -3 479 | acc +2 480 | acc +23 481 | jmp -256 482 | acc +33 483 | jmp -473 484 | acc +29 485 | acc -13 486 | jmp +77 487 | jmp +158 488 | acc -16 489 | jmp -10 490 | jmp -181 491 | jmp -135 492 | nop -95 493 | acc +46 494 | acc +39 495 | acc -3 496 | jmp -94 497 | jmp -67 498 | acc +49 499 | nop -78 500 | nop -9 501 | jmp +107 502 | acc -19 503 | acc -1 504 | acc +0 505 | acc -4 506 | jmp -189 507 | acc +11 508 | jmp -106 509 | jmp -200 510 | jmp +122 511 | acc +8 512 | acc +48 513 | acc +15 514 | acc +0 515 | jmp -493 516 | acc +13 517 | jmp -8 518 | acc +36 519 | acc -10 520 | jmp +1 521 | acc +9 522 | jmp +7 523 | jmp +85 524 | acc +22 525 | acc -8 526 | nop -124 527 | jmp -517 528 | jmp -338 529 | acc +39 530 | nop -438 531 | acc -11 532 | jmp +69 533 | acc +8 534 | acc +34 535 | acc +34 536 | acc -9 537 | jmp -205 538 | nop -528 539 | jmp -495 540 | acc +47 541 | acc +40 542 | acc +30 543 | jmp -328 544 | acc -2 545 | acc +41 546 | jmp -475 547 | acc +42 548 | acc +48 549 | acc +2 550 | acc +7 551 | jmp -415 552 | nop -249 553 | acc -3 554 | jmp +65 555 | acc +23 556 | nop -4 557 | jmp -254 558 | acc -12 559 | acc +22 560 | acc +27 561 | jmp -176 562 | jmp -408 563 | acc -15 564 | acc +14 565 | acc +30 566 | acc +0 567 | jmp -363 568 | jmp -426 569 | acc +38 570 | nop -425 571 | jmp -440 572 | jmp +1 573 | acc +22 574 | jmp -63 575 | jmp -406 576 | nop -445 577 | acc -5 578 | acc +34 579 | nop -425 580 | jmp +65 581 | acc +33 582 | jmp -91 583 | acc -12 584 | jmp +1 585 | jmp -541 586 | nop -489 587 | jmp -490 588 | acc +20 589 | acc +20 590 | acc +38 591 | acc -18 592 | jmp -548 593 | acc +43 594 | acc -7 595 | jmp -351 596 | acc -9 597 | acc +50 598 | acc +1 599 | nop -587 600 | jmp -230 601 | jmp +1 602 | nop +43 603 | jmp -65 604 | acc +31 605 | acc +5 606 | acc +1 607 | jmp -105 608 | nop -477 609 | acc +21 610 | nop -92 611 | jmp -263 612 | acc +28 613 | jmp -265 614 | jmp -311 615 | acc +2 616 | acc +23 617 | acc +50 618 | jmp -4 619 | acc +42 620 | acc +42 621 | acc +31 622 | jmp -167 623 | acc +49 624 | acc +46 625 | jmp -73 626 | nop -135 627 | acc +43 628 | jmp -236 629 | acc -14 630 | acc -3 631 | jmp -406 632 | acc +2 633 | acc -3 634 | acc +47 635 | jmp -420 636 | acc -8 637 | acc +18 638 | jmp -604 639 | jmp -218 640 | acc +37 641 | acc -16 642 | nop -278 643 | acc -15 644 | jmp -214 645 | acc -6 646 | acc +18 647 | acc +7 648 | acc +0 649 | jmp -252 650 | acc +14 651 | jmp -266 652 | acc +27 653 | acc -16 654 | nop -533 655 | nop -534 656 | jmp +1 657 | -------------------------------------------------------------------------------- /posts/2020/advent-of-code-day-6.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Advent of Code - Day 6: Custom Customs\n" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## Part One\n", 15 | "\n", 16 | "As your flight approaches the regional airport where you'll switch to a much\n", 17 | "larger plane,\n", 18 | "[customs declaration forms](https://en.wikipedia.org/wiki/Customs_declaration)\n", 19 | "are distributed to the passengers.\n", 20 | "\n", 21 | "The form asks a series of 26 yes-or-no questions marked a through z. All you\n", 22 | "need to do is identify the questions for which anyone in your group answers\n", 23 | "\"yes\". Since your group is just you, this doesn't take very long.\n", 24 | "\n", 25 | "However, the person sitting next to you seems to be experiencing a language\n", 26 | "barrier and asks if you can help. For each of the people in their group, you\n", 27 | "write down the questions for which they answer \"yes\", one per line. For example:\n", 28 | "\n", 29 | "```\n", 30 | "abcx\n", 31 | "abcy\n", 32 | "abcz\n", 33 | "```\n", 34 | "\n", 35 | "In this group, there are 6 questions to which anyone answered \"yes\": a, b, c, x,\n", 36 | "y, and z. (Duplicate answers to the same question don't count extra; each\n", 37 | "question counts at most once.)\n", 38 | "\n", 39 | "Another group asks for your help, then another, and eventually you've collected\n", 40 | "answers from every group on the plane (your puzzle input). Each group's answers\n", 41 | "are separated by a blank line, and within each group, each person's answers are\n", 42 | "on a single line. For example:\n", 43 | "\n", 44 | "```\n", 45 | "abc\n", 46 | "\n", 47 | "a\n", 48 | "b\n", 49 | "c\n", 50 | "\n", 51 | "ab\n", 52 | "ac\n", 53 | "\n", 54 | "a\n", 55 | "a\n", 56 | "a\n", 57 | "a\n", 58 | "\n", 59 | "b\n", 60 | "\n", 61 | "```\n", 62 | "\n", 63 | "This list represents answers from five groups:\n", 64 | "\n", 65 | "- The first group contains one person who answered \"yes\" to 3 questions: a, b,\n", 66 | " and c.\n", 67 | "- The second group contains three people; combined, they answered \"yes\" to 3\n", 68 | " questions: a, b, and c.\n", 69 | "- The third group contains two people; combined, they answered \"yes\" to 3\n", 70 | " questions: a, b, and c.\n", 71 | "- The fourth group contains four people; combined, they answered \"yes\" to only 1\n", 72 | " question, a.\n", 73 | "- The last group contains one person who answered \"yes\" to only 1 question, b.\n", 74 | "\n", 75 | "In this example, the sum of these counts is 3 + 3 + 3 + 1 + 1 = 11.\n", 76 | "\n", 77 | "For each group, count the number of questions to which anyone answered \"yes\".\n", 78 | "**What is the sum of those counts?**\n" 79 | ] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "metadata": {}, 84 | "source": [ 85 | "### Load Input data\n" 86 | ] 87 | }, 88 | { 89 | "cell_type": "markdown", 90 | "metadata": {}, 91 | "source": [ 92 | ":::{note} The input data can be found\n", 93 | "[here](https://adventofcode.com/2020/day/6). :::\n" 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": 1, 99 | "metadata": {}, 100 | "outputs": [ 101 | { 102 | "name": "stdout", 103 | "output_type": "stream", 104 | "text": [ 105 | "461\n", 106 | "[['b', 'b', 'b', 'b'], ['x', 'xfkj', 'xb'], ['ovuxdgiheszjbaltw', 'oxwjiubhfylzavst'], ['se', 'u', 'j', 'se'], ['eaxzstqkujdlhi', 'dsinkoqhjxz']]\n" 107 | ] 108 | } 109 | ], 110 | "source": [ 111 | "def parse_input_data(data):\n", 112 | " data = data.split('\\n\\n')\n", 113 | " return [x.strip().split() for x in data]\n", 114 | "\n", 115 | "\n", 116 | "with open('../../data/advent-of-code/2020/day-6-input') as fid:\n", 117 | " data = fid.read()\n", 118 | " data = parse_input_data(data)\n", 119 | "\n", 120 | "print(len(data))\n", 121 | "print(data[0:5])" 122 | ] 123 | }, 124 | { 125 | "cell_type": "markdown", 126 | "metadata": {}, 127 | "source": [ 128 | "### Solution\n" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": 2, 134 | "metadata": {}, 135 | "outputs": [], 136 | "source": [ 137 | "def counter(group):\n", 138 | " return len(set(''.join(group)))" 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": 3, 144 | "metadata": {}, 145 | "outputs": [ 146 | { 147 | "data": { 148 | "text/plain": [ 149 | "[['abc'], ['a', 'b', 'c'], ['ab', 'ac'], ['a', 'a', 'a', 'a'], ['b']]" 150 | ] 151 | }, 152 | "execution_count": 3, 153 | "metadata": {}, 154 | "output_type": "execute_result" 155 | } 156 | ], 157 | "source": [ 158 | "test_sample = parse_input_data(\n", 159 | " \"\"\"abc\n", 160 | "\n", 161 | "a\n", 162 | "b\n", 163 | "c\n", 164 | "\n", 165 | "ab\n", 166 | "ac\n", 167 | "\n", 168 | "a\n", 169 | "a\n", 170 | "a\n", 171 | "a\n", 172 | "\n", 173 | "b\"\"\"\n", 174 | ")\n", 175 | "test_sample" 176 | ] 177 | }, 178 | { 179 | "cell_type": "code", 180 | "execution_count": 4, 181 | "metadata": {}, 182 | "outputs": [ 183 | { 184 | "data": { 185 | "text/plain": [ 186 | "[3, 3, 3, 1, 1]" 187 | ] 188 | }, 189 | "execution_count": 4, 190 | "metadata": {}, 191 | "output_type": "execute_result" 192 | } 193 | ], 194 | "source": [ 195 | "[counter(group) for group in test_sample]" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": 5, 201 | "metadata": {}, 202 | "outputs": [ 203 | { 204 | "data": { 205 | "text/plain": [ 206 | "6534" 207 | ] 208 | }, 209 | "execution_count": 5, 210 | "metadata": {}, 211 | "output_type": "execute_result" 212 | } 213 | ], 214 | "source": [ 215 | "sum(counter(group) for group in data)" 216 | ] 217 | }, 218 | { 219 | "cell_type": "markdown", 220 | "metadata": {}, 221 | "source": [ 222 | "## Part Two\n", 223 | "\n", 224 | "As you finish the last group's customs declaration, you notice that you misread\n", 225 | "one word in the instructions:\n", 226 | "\n", 227 | "You don't need to identify the questions to which anyone answered \"yes\"; you\n", 228 | "need to identify the questions to which everyone answered \"yes\"!\n", 229 | "\n", 230 | "Using the same example as above:\n", 231 | "\n", 232 | "```\n", 233 | "abc\n", 234 | "\n", 235 | "a\n", 236 | "b\n", 237 | "c\n", 238 | "\n", 239 | "ab\n", 240 | "ac\n", 241 | "\n", 242 | "a\n", 243 | "a\n", 244 | "a\n", 245 | "a\n", 246 | "\n", 247 | "b\n", 248 | "\n", 249 | "```\n", 250 | "\n", 251 | "This list represents answers from five groups:\n", 252 | "\n", 253 | "- In the first group, everyone (all 1 person) answered \"yes\" to 3 questions: a,\n", 254 | " b, and c.\n", 255 | "- In the second group, there is no question to which everyone answered \"yes\".\n", 256 | "- In the third group, everyone answered yes to only 1 question, a. Since some\n", 257 | " people did not answer \"yes\" to b or c, they don't count.\n", 258 | "- In the fourth group, everyone answered yes to only 1 question, a.\n", 259 | "- In the fifth group, everyone (all 1 person) answered \"yes\" to 1 question, b.\n", 260 | "\n", 261 | "In this example, the sum of these counts is 3 + 0 + 1 + 1 + 1 = 6.\n", 262 | "\n", 263 | "For each group, count the number of questions to which everyone answered \"yes\".\n", 264 | "**What is the sum of those counts?**\n" 265 | ] 266 | }, 267 | { 268 | "cell_type": "markdown", 269 | "metadata": {}, 270 | "source": [ 271 | "### Solution\n" 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "execution_count": 6, 277 | "metadata": {}, 278 | "outputs": [], 279 | "source": [ 280 | "def counter(group):\n", 281 | " answers = set(''.join(group))\n", 282 | " inter = set(group[0])\n", 283 | " for entry in group[1:]:\n", 284 | " inter = inter.intersection(set(entry))\n", 285 | " return len(inter)" 286 | ] 287 | }, 288 | { 289 | "cell_type": "code", 290 | "execution_count": 7, 291 | "metadata": {}, 292 | "outputs": [ 293 | { 294 | "data": { 295 | "text/plain": [ 296 | "[3, 0, 1, 1, 1]" 297 | ] 298 | }, 299 | "execution_count": 7, 300 | "metadata": {}, 301 | "output_type": "execute_result" 302 | } 303 | ], 304 | "source": [ 305 | "[counter(group) for group in test_sample]" 306 | ] 307 | }, 308 | { 309 | "cell_type": "code", 310 | "execution_count": 8, 311 | "metadata": {}, 312 | "outputs": [ 313 | { 314 | "data": { 315 | "text/plain": [ 316 | "3402" 317 | ] 318 | }, 319 | "execution_count": 8, 320 | "metadata": {}, 321 | "output_type": "execute_result" 322 | } 323 | ], 324 | "source": [ 325 | "sum(counter(group) for group in data)" 326 | ] 327 | } 328 | ], 329 | "metadata": { 330 | "author": "Anderson Banihirwe", 331 | "date": "2020-12-06", 332 | "kernelspec": { 333 | "display_name": "Python 3", 334 | "language": "python", 335 | "name": "python3" 336 | }, 337 | "language_info": { 338 | "codemirror_mode": { 339 | "name": "ipython", 340 | "version": 3 341 | }, 342 | "file_extension": ".py", 343 | "mimetype": "text/x-python", 344 | "name": "python", 345 | "nbconvert_exporter": "python", 346 | "pygments_lexer": "ipython3", 347 | "version": "3.8.8" 348 | }, 349 | "tags": "python,adventofcode", 350 | "title": "Advent of Code - Day 6: Custom Customs", 351 | "widgets": { 352 | "application/vnd.jupyter.widget-state+json": { 353 | "state": {}, 354 | "version_major": 2, 355 | "version_minor": 0 356 | } 357 | } 358 | }, 359 | "nbformat": 4, 360 | "nbformat_minor": 4 361 | } 362 | -------------------------------------------------------------------------------- /posts/2020/advent-of-code-day-5.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Advent of Code - Day 5: Binary Boarding\n" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## Part One\n", 15 | "\n", 16 | "You board your plane only to discover a new problem: you dropped your boarding\n", 17 | "pass! You aren't sure which seat is yours, and all of the flight attendants are\n", 18 | "busy with the flood of people that suddenly made it through passport control.\n", 19 | "\n", 20 | "You write a quick program to use your phone's camera to scan all of the nearby\n", 21 | "boarding passes (your puzzle input); perhaps you can find your seat through\n", 22 | "process of elimination.\n", 23 | "\n", 24 | "Instead of [zones or groups](https://www.youtube.com/watch?v=oAHbLRjF0vo), this\n", 25 | "airline uses binary space partitioning to seat people. A seat might be specified\n", 26 | "like FBFBBFFRLR, where F means \"front\", B means \"back\", L means \"left\", and R\n", 27 | "means \"right\".\n", 28 | "\n", 29 | "The first 7 characters will either be F or B; these specify exactly one of the\n", 30 | "128 rows on the plane (numbered 0 through 127). Each letter tells you which half\n", 31 | "of a region the given seat is in. Start with the whole list of rows; the first\n", 32 | "letter indicates whether the seat is in the front (0 through 63) or the back (64\n", 33 | "through 127). The next letter indicates which half of that region the seat is\n", 34 | "in, and so on until you're left with exactly one row.\n", 35 | "\n", 36 | "For example, consider just the first seven characters of FBFBBFFRLR:\n", 37 | "\n", 38 | "Start by considering the whole range, rows 0 through 127.\n", 39 | "\n", 40 | "- F means to take the lower half, keeping rows 0 through 63.\n", 41 | "- B means to take the upper half, keeping rows 32 through 63.\n", 42 | "- F means to take the lower half, keeping rows 32 through 47.\n", 43 | "- B means to take the upper half, keeping rows 40 through 47.\n", 44 | "- B keeps rows 44 through 47.\n", 45 | "- F keeps rows 44 through 45.\n", 46 | "\n", 47 | "The final F keeps the lower of the two, row 44. The last three characters will\n", 48 | "be either L or R; these specify exactly one of the 8 columns of seats on the\n", 49 | "plane (numbered 0 through 7). The same process as above proceeds again, this\n", 50 | "time with only three steps. L means to keep the lower half, while R means to\n", 51 | "keep the upper half.\n", 52 | "\n", 53 | "For example, consider just the last 3 characters of FBFBBFFRLR:\n", 54 | "\n", 55 | "- Start by considering the whole range, columns 0 through 7.\n", 56 | "- R means to take the upper half, keeping columns 4 through 7.\n", 57 | "- L means to take the lower half, keeping columns 4 through 5.\n", 58 | "- The final R keeps the upper of the two, column 5. So, decoding FBFBBFFRLR\n", 59 | " reveals that it is the seat at row 44, column 5.\n", 60 | "\n", 61 | "Every seat also has a unique seat ID: multiply the row by 8, then add the\n", 62 | "column. In this example, the seat has ID 44 \\* 8 + 5 = 357.\n", 63 | "\n", 64 | "Here are some other boarding passes:\n", 65 | "\n", 66 | "- BFFFBBFRRR: row 70, column 7, seat ID 567.\n", 67 | "- FFFBBBFRRR: row 14, column 7, seat ID 119.\n", 68 | "- BBFFBBFRLL: row 102, column 4, seat ID 820.\n", 69 | "\n", 70 | "As a sanity check, look through your list of boarding passes. **What is the\n", 71 | "highest seat ID on a boarding pass?**\n" 72 | ] 73 | }, 74 | { 75 | "cell_type": "markdown", 76 | "metadata": {}, 77 | "source": [ 78 | "### Load Input data\n" 79 | ] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "metadata": {}, 84 | "source": [ 85 | ":::{note} The input data can be found\n", 86 | "[here](https://adventofcode.com/2020/day/5). :::\n" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": 1, 92 | "metadata": {}, 93 | "outputs": [ 94 | { 95 | "name": "stdout", 96 | "output_type": "stream", 97 | "text": [ 98 | "846\n", 99 | "FBBFFBBLLL\n", 100 | "\n" 101 | ] 102 | } 103 | ], 104 | "source": [ 105 | "with open('../../data/advent-of-code/2020/day-5-input') as fid:\n", 106 | " data = fid.readlines()\n", 107 | "\n", 108 | "print(len(data))\n", 109 | "print(data[0])" 110 | ] 111 | }, 112 | { 113 | "cell_type": "markdown", 114 | "metadata": {}, 115 | "source": [ 116 | "### Solution\n" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": 2, 122 | "metadata": {}, 123 | "outputs": [], 124 | "source": [ 125 | "import math\n", 126 | "\n", 127 | "\n", 128 | "def find_seat_info(seat_number, nrows, ncols, row_identifiers, col_identifiers):\n", 129 | " \"\"\"Function to decode the seat_id when given the seat number\"\"\"\n", 130 | "\n", 131 | " def decode_seat(seat, n_entries, identifiers):\n", 132 | " lo, hi = 0, n_entries\n", 133 | " for letter in seat:\n", 134 | " if letter == identifiers[0]:\n", 135 | " lo, hi = lo, math.floor((lo + hi) / 2)\n", 136 | " elif letter == identifiers[1]:\n", 137 | " lo, hi = math.ceil((lo + hi) / 2), hi\n", 138 | "\n", 139 | " return lo\n", 140 | "\n", 141 | " row = decode_seat(seat_number[:7], nrows, row_identifiers)\n", 142 | " col = decode_seat(seat_number[7:], ncols, col_identifiers)\n", 143 | " return {\n", 144 | " 'seat_number': seat_number,\n", 145 | " 'row': row,\n", 146 | " 'column': col,\n", 147 | " 'seat_id': row * 8 + col,\n", 148 | " }" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": 3, 154 | "metadata": {}, 155 | "outputs": [], 156 | "source": [ 157 | "nrows, ncols = 128, 8\n", 158 | "row_identifiers = ('F', 'B')\n", 159 | "col_identifiers = ('L', 'R')" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": 4, 165 | "metadata": {}, 166 | "outputs": [ 167 | { 168 | "data": { 169 | "text/plain": [ 170 | "{'seat_number': 'FBFBBFFRLR', 'row': 44, 'column': 5, 'seat_id': 357}" 171 | ] 172 | }, 173 | "execution_count": 4, 174 | "metadata": {}, 175 | "output_type": "execute_result" 176 | } 177 | ], 178 | "source": [ 179 | "# Test our function on sample input seat number\n", 180 | "find_seat_info('FBFBBFFRLR', nrows, ncols, row_identifiers, col_identifiers)" 181 | ] 182 | }, 183 | { 184 | "cell_type": "markdown", 185 | "metadata": {}, 186 | "source": [ 187 | "Everything appears to be working.... Let's loop over all the seat numbers and\n", 188 | "collect the ids:\n" 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": 5, 194 | "metadata": {}, 195 | "outputs": [ 196 | { 197 | "data": { 198 | "text/plain": [ 199 | "(12, 858)" 200 | ] 201 | }, 202 | "execution_count": 5, 203 | "metadata": {}, 204 | "output_type": "execute_result" 205 | } 206 | ], 207 | "source": [ 208 | "ids = [\n", 209 | " find_seat_info(seat, nrows, ncols, row_identifiers, col_identifiers)['seat_id'] for seat in data\n", 210 | "]\n", 211 | "min(ids), max(ids)" 212 | ] 213 | }, 214 | { 215 | "cell_type": "markdown", 216 | "metadata": {}, 217 | "source": [ 218 | "## Part Two\n", 219 | "\n", 220 | "Ding! The \"fasten seat belt\" signs have turned on. Time to find your seat.\n", 221 | "\n", 222 | "It's a completely full flight, so your seat should be the only missing boarding\n", 223 | "pass in your list. However, there's a catch: some of the seats at the very front\n", 224 | "and back of the plane don't exist on this aircraft, so they'll be missing from\n", 225 | "your list as well.\n", 226 | "\n", 227 | "Your seat wasn't at the very front or back, though; the seats with IDs +1 and -1\n", 228 | "from yours will be in your list.\n", 229 | "\n", 230 | "**What is the ID of your seat?**\n" 231 | ] 232 | }, 233 | { 234 | "cell_type": "markdown", 235 | "metadata": {}, 236 | "source": [ 237 | "### Solution\n" 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": 6, 243 | "metadata": {}, 244 | "outputs": [ 245 | { 246 | "data": { 247 | "text/plain": [ 248 | "178" 249 | ] 250 | }, 251 | "execution_count": 6, 252 | "metadata": {}, 253 | "output_type": "execute_result" 254 | } 255 | ], 256 | "source": [ 257 | "# Find missing ids\n", 258 | "missing_ids = [seat_id for seat_id in range(5, nrows * 8 + 5) if seat_id not in set(ids)]\n", 259 | "len(missing_ids)" 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": 7, 265 | "metadata": {}, 266 | "outputs": [ 267 | { 268 | "data": { 269 | "text/plain": [ 270 | "[11, 557, 859]" 271 | ] 272 | }, 273 | "execution_count": 7, 274 | "metadata": {}, 275 | "output_type": "execute_result" 276 | } 277 | ], 278 | "source": [ 279 | "# Loop over missing ids, and find possible candidates\n", 280 | "possible_candidates = []\n", 281 | "for idx in range(1, len(missing_ids) - 1):\n", 282 | " if not (\n", 283 | " (missing_ids[idx - 1] == missing_ids[idx] - 1)\n", 284 | " and (missing_ids[idx + 1] == missing_ids[idx] + 1)\n", 285 | " ):\n", 286 | " possible_candidates.append(missing_ids[idx])\n", 287 | "possible_candidates" 288 | ] 289 | }, 290 | { 291 | "cell_type": "markdown", 292 | "metadata": {}, 293 | "source": [ 294 | "We can use a process of elimination to get the ID of our seat by checking for\n", 295 | "the ids of the front and back seats:\n" 296 | ] 297 | }, 298 | { 299 | "cell_type": "code", 300 | "execution_count": 8, 301 | "metadata": {}, 302 | "outputs": [ 303 | { 304 | "data": { 305 | "text/plain": [ 306 | "(12, 858)" 307 | ] 308 | }, 309 | "execution_count": 8, 310 | "metadata": {}, 311 | "output_type": "execute_result" 312 | } 313 | ], 314 | "source": [ 315 | "min(ids), max(ids)" 316 | ] 317 | }, 318 | { 319 | "cell_type": "markdown", 320 | "metadata": {}, 321 | "source": [ 322 | "Since it was pointed out that our seat wasn't at the very front or back, we can\n", 323 | "eliminate `11` and `859`. So, the id our seat is **`557`**\n" 324 | ] 325 | } 326 | ], 327 | "metadata": { 328 | "author": "Anderson Banihirwe", 329 | "date": "2020-12-05", 330 | "kernelspec": { 331 | "display_name": "Python 3", 332 | "language": "python", 333 | "name": "python3" 334 | }, 335 | "language_info": { 336 | "codemirror_mode": { 337 | "name": "ipython", 338 | "version": 3 339 | }, 340 | "file_extension": ".py", 341 | "mimetype": "text/x-python", 342 | "name": "python", 343 | "nbconvert_exporter": "python", 344 | "pygments_lexer": "ipython3", 345 | "version": "3.8.8" 346 | }, 347 | "tags": "python,adventofcode", 348 | "title": "Advent of Code - Day 5: Binary Boarding", 349 | "widgets": { 350 | "application/vnd.jupyter.widget-state+json": { 351 | "state": {}, 352 | "version_major": 2, 353 | "version_minor": 0 354 | } 355 | } 356 | }, 357 | "nbformat": 4, 358 | "nbformat_minor": 4 359 | } 360 | -------------------------------------------------------------------------------- /data/advent-of-code/2020/day-3-input: -------------------------------------------------------------------------------- 1 | ........#.............#........ 2 | ...#....#...#....#............. 3 | .#..#...#............#.....#..# 4 | ..#......#..##............###.. 5 | ..........#......#..#..#....... 6 | .#..#.......#.........#.#...... 7 | .........#..#....##..#.##....#. 8 | ..#....##...#.................. 9 | ##..........#.##...#....##..#.. 10 | ...#....#...#..............#... 11 | ...........................#..# 12 | ..##.##.#..................#... 13 | ...#.##..#............#........ 14 | ........#.......#...#.....##.#. 15 | .##..........#......#.......#.. 16 | ...#..........#...#..#.......#. 17 | ......#...#...#.##.......#.#... 18 | ........#...#...#...##......... 19 | #..............#.#....#.......# 20 | ..#..#..#.#....#............... 21 | .....#........#...#..........#. 22 | ##......#...#..#.##.......#.... 23 | ..#.#.....#.#.............#.#.# 24 | #..#..##......##...#........... 25 | ..#......#........#.....#...... 26 | .....#.......#....#.#...#...... 27 | ...#........#...........#...#.. 28 | .......#.#...........###....#.. 29 | ...#...........##....##........ 30 | #....#..####....#.....#..#....# 31 | ..........#...........#........ 32 | ...#.......#....#.#.........#.. 33 | ....#...#.......#..###......... 34 | ......#......#..#......#..#.... 35 | ...#.....#............#..#..... 36 | ...#.#.#.#..#.......#.....#.... 37 | #....##...#.........#...##..... 38 | #..#.......#..#..#..#...##..... 39 | #.......#............#.....#... 40 | .#........##....##...#........# 41 | .....#...#..................... 42 | .......#........#.............. 43 | .....#............#.#.#...#.#.. 44 | .....##..#.............#....... 45 | ..#.##..#........#..#...#...... 46 | .........#.#....#...........#.. 47 | .#.....#..#....#.....#...#..... 48 | ....#.#................#....... 49 | ...............##......#...#... 50 | .##...#...#.......##.#....#.... 51 | ............#........#.......#. 52 | ......##.#.#................... 53 | .#.#..............#.......#.... 54 | #.....#...#.......#..#...#..... 55 | .............#....#..#......#.. 56 | ........#...##................# 57 | .......#...#..#..##............ 58 | ..#..#...##...#..#.#.....#...#. 59 | .#.#...#.........#.#........... 60 | ...###....#.......#...#........ 61 | ........#......##.#...#..##..#. 62 | .....................#.#....... 63 | .............#...........#...#. 64 | #..#..#.....#.#...#............ 65 | ...#....#.....#...........#.... 66 | ..##.....##...#......#..##..... 67 | #.....#.....###.#.....#....##.. 68 | .#...........###............... 69 | ..................#..##.#...#.. 70 | ................#....##.#...... 71 | .#.#.#...#....#.........#..#.#. 72 | #.......#........##............ 73 | .......##.#....#.#............# 74 | ..........#..##.#....#......... 75 | ........##..#....#............. 76 | .........#....#...........##... 77 | #.........#.#..#..#..........#. 78 | .....#........#......#......... 79 | ....#.#.#...............#...... 80 | .#..#..##...#.##..........#.... 81 | ..#....................#.#..... 82 | .........#....#...........#.#.# 83 | ........#....##.##............. 84 | ..#.....#.......#..#......#.... 85 | #..........#.#.....#.#....#.... 86 | ........##.#.....#..#.....#.#.. 87 | ...................#...#....#.# 88 | ............#..#....#...#...#.. 89 | ..............#.#.........#.... 90 | ...#..#..#.#..##..##........... 91 | .#...........................#. 92 | .#.......#...........#....#.#.# 93 | ......#..#...#........#...##... 94 | .........#......#.#.......#...# 95 | ...#..##................#...... 96 | .............#.#..##....#.#.... 97 | ...............#..#......#..... 98 | .#......#.#.#....#........#.... 99 | ........#..#.##..#..#.........# 100 | ...#....#.#...#..#.......#..#.. 101 | ..#...##.........#..#...#...... 102 | ...#...........#.............#. 103 | ....#.....................#.... 104 | .....#..#...............#.#...# 105 | ....#..........#........#...... 106 | ..#....#........##..##......... 107 | ...#....#..#.#.......#...#..... 108 | ..#........#....#...##....#.#.. 109 | .#...#........##.....#....###.. 110 | #....#....##......#........#... 111 | .........#..#.#..........#....# 112 | ....#...#.....#.......##....... 113 | ..............#..........#.##.. 114 | #...#..#..............#......#. 115 | .................#......##....# 116 | ..#..##..#.......#..#.#......#. 117 | .............#........#.....#.# 118 | .#.##............#..#.......... 119 | ..#...#...........#..##........ 120 | .#....#...#....#.......#....... 121 | ...#.#..#..#..#....#.....#..#.. 122 | ....#..##..............#...#... 123 | #..........###......###........ 124 | .##.##......#..#............#.. 125 | .#...........#.#.....#...#..... 126 | #.#..#...#............#........ 127 | .........#...#...#..........##. 128 | .......###..#..........#....... 129 | ...........###.....#........#.. 130 | .#.............#.....#......#.. 131 | ...#.....#....#.#.........##... 132 | ....##..##...#.......##........ 133 | ......#....##.........#......#. 134 | ..........#.....##..#.....#..#. 135 | ..........####...#..#.........# 136 | .##....#..#.#...#.......#...... 137 | ...#.#.##.#.#...#....#.#.#..... 138 | .........#...##........##.....# 139 | ..#........#..........##...##.# 140 | ##...##..........#.#........... 141 | ..............#......#......... 142 | ........#.....#.#.......#...... 143 | .#...#.....#....#.#..#......... 144 | .....#....................##... 145 | ....#..................#.#...## 146 | .....#............#..##........ 147 | #..........#....#.#.......##.#. 148 | ....#..#.....................#. 149 | #..#....##.....#............... 150 | ..#...#..#..##....#.#.......... 151 | .......#......#.#.......#.....# 152 | ...#.#.......#...#.##.......... 153 | ....#..........#....#.#.#...... 154 | .......#..#..........#..##..... 155 | #......#......#...#......#...#. 156 | ###..#....##......##........#.. 157 | .#..........#.....#.......#.#.. 158 | .......#.....#.....#.#......... 159 | ..#...#....#................... 160 | ..............#.##............. 161 | .#...#.......#.##...#.#.......# 162 | .......#......................# 163 | ....#.#...#.#........#......... 164 | .#......#....#...#............. 165 | #.......#...###.....#.#.#..#... 166 | #....##.#...............##..... 167 | ..#.......#..................#. 168 | .....####...............#...... 169 | .##......#......#.#.......##.#. 170 | #......##..###....#....#......# 171 | .##.......##.##...#.##......... 172 | ......##............#.......#.. 173 | ......#..#.....##.#............ 174 | .#..........#.....##........... 175 | #.........#......#......##.#... 176 | .........#.......#..#......#.#. 177 | .........#.......#...........#. 178 | .#..##.#..................##... 179 | .............#.............#... 180 | .....##........#......##...##.. 181 | ..#..#.#.....#..#....#......... 182 | .....#....#.....#.....#........ 183 | #......##.....#....#....#...... 184 | #.................#..#.#......# 185 | .......#..#......#....#.#...#.# 186 | ....#.........#..#..........#.# 187 | ##......#............#...#...#. 188 | ....##......#...#.....#....##.. 189 | .#...##.........#.............. 190 | ......#.....................#.. 191 | ..#..........###....#.......... 192 | #....#...#..#.............#.... 193 | #........#.#......#....#....... 194 | .#...#.......#..#...#.#...#..#. 195 | ................##.#.....#..... 196 | ###.......#...#................ 197 | ...#.......#...#.#.....#....... 198 | ..#.........#.....#.#.......#.. 199 | ......#.......................# 200 | #.....#.#..#....#.......#...... 201 | ...#....#..#....####........... 202 | .............#.....#...##...... 203 | .......#.........#...#..#...... 204 | .##..#.........#....#.#........ 205 | ....##...#.#...........#....#.. 206 | .........................##.... 207 | ..###.......##....#.#.........# 208 | .#....#.#.#...........##....#.. 209 | ......#...#..#..#..#..#.......# 210 | ..#....#.#.......#..#..#..#...# 211 | .....##...#.##....#.#...#...... 212 | .........#..#....#..#.......... 213 | .##..##.........#.#.....#...... 214 | ..........#...##...#.#...#..... 215 | #.##..#..#.............#....... 216 | ...#...........#.......#......# 217 | .......#....#....#...##.......# 218 | ..#.##........###..#......#.... 219 | ...#...........###......#..#..# 220 | .#.........#.#.........#.#..... 221 | ##.......##.##.##......##...... 222 | ............#...#..........#... 223 | ....................#.......... 224 | ...#..#...........#...#...#.... 225 | .................#...#......### 226 | ...#................#.#.##..... 227 | ...............#........#...... 228 | #.............##......#.#..#... 229 | ..#.#.....#..#.##.....##...#... 230 | ......#.........#......#....... 231 | #.......#......#....#........#. 232 | .#..##.....#.........#......... 233 | ....##.##.#...#.........##.#... 234 | ...............#..#..#..##..... 235 | .#..#...............###........ 236 | .##............##.............. 237 | ...............#...##...#...#.# 238 | ..#.#......#.#..#.............# 239 | #.#..#..##.........#.#.#...#... 240 | ....##.#....................##. 241 | .........#..#.....#.....#..#..# 242 | ....#......#......#.##....#.... 243 | ........###..#.............#..# 244 | ##................#.........#.. 245 | #.....#.......#....#........... 246 | ..#.......#..#........#....#... 247 | ..#.#.##..#.#...##........#.##. 248 | ..#..........#............#.... 249 | ..........#...............##... 250 | ..........###........#.#....... 251 | .....###..#.............#...... 252 | ##.............#...#.....#..... 253 | .....#......#....#........#.#.. 254 | ............#..#..............# 255 | .................#...........## 256 | #........#.........###.....#... 257 | ..#.#..............##......#.#. 258 | .#...........#.........#..##..# 259 | ............................... 260 | .#.....#..#....#....#......#... 261 | .#...#......#.#..#....#.......# 262 | ......#.##.......#......#...... 263 | ......#..###..#................ 264 | #..#.....#........##...#....... 265 | ......##.........##....#...##.. 266 | .#..........#.................# 267 | #..#.......#...............#... 268 | .........#..###....#.#.##.#.... 269 | ..#...#.##..##...............## 270 | .........#..................... 271 | .#....##...#......#....#....... 272 | ............#..........#..#.... 273 | ...#......##....#....#........# 274 | .#...................#......... 275 | #.#........###....#..........#. 276 | .........#....#....#........##. 277 | .#....#..#.........#..#........ 278 | ...............#..#...#..#...## 279 | .........#....##....#......#... 280 | .#............................. 281 | ...#........#...#.#...#.#..#... 282 | .....#..##...#.#............... 283 | #.....#....#.........#......... 284 | #...#...........##.........#... 285 | ..##........#.#...#...#......#. 286 | ...........#.....#...#.#....... 287 | ......###....#.....#........... 288 | ......##...#..........#....#.#. 289 | .......##..##..........#....... 290 | ....#............#..#....##.... 291 | ..##...................#.#..... 292 | ...#.#..#.#.................... 293 | .#..##..#............##.###..#. 294 | #.#...#....#.#..........#.#.... 295 | ........#....#.....#........... 296 | ..##....#...#.......#.......... 297 | ...........##.##....#.......... 298 | .....#............#............ 299 | .......#.............#....#.... 300 | .................#......#...... 301 | ......##.......#....#..##...#.. 302 | .#..#....#..................... 303 | ...#.#.#...#......##........... 304 | ##........##.#....#....#....... 305 | .......#.....#..#..#...#.##.... 306 | #..........#....#.#..#..#..#... 307 | ...##..............#........... 308 | .........#.....#.#....#.......# 309 | .........#....##..#..##..#..... 310 | .....#......................#.. 311 | ...###...#..#......#........... 312 | ....#.....................#.... 313 | ............................... 314 | ..#.....###.......#..#....#.... 315 | #..........#.................#. 316 | ......#.......###.......#..##.. 317 | .............#.##.............. 318 | ......#..#.#..#...........#.... 319 | ...#....##.#...#..#.#...#....#. 320 | ..................#...#....#.## 321 | ......#.#....#................. 322 | ......#.#.....#.....#..##...... 323 | #..##...........#..#.....#.##.. 324 | -------------------------------------------------------------------------------- /posts/2020/advent-of-code-day-9.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Advent of Code - Day 9: Encoding Error\n" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## Part One\n", 15 | "\n", 16 | "With your neighbor happily enjoying their video game, you turn your attention to\n", 17 | "an open data port on the little screen in the seat in front of you.\n", 18 | "\n", 19 | "Though the port is non-standard, you manage to connect it to your computer\n", 20 | "through the clever use of several paperclips. Upon connection, the port outputs\n", 21 | "a series of numbers (your puzzle input).\n", 22 | "\n", 23 | "The data appears to be encrypted with the eXchange-Masking Addition System\n", 24 | "(XMAS) which, conveniently for you, is an old cypher with an important weakness.\n", 25 | "\n", 26 | "XMAS starts by transmitting a preamble of 25 numbers. After that, each number\n", 27 | "you receive should be the sum of any two of the 25 immediately previous numbers.\n", 28 | "The two numbers will have different values, and there might be more than one\n", 29 | "such pair.\n", 30 | "\n", 31 | "For example, suppose your preamble consists of the numbers 1 through 25 in a\n", 32 | "random order. To be valid, the next number must be the sum of two of those\n", 33 | "numbers:\n", 34 | "\n", 35 | "- 26 would be a valid next number, as it could be 1 plus 25 (or many other\n", 36 | " pairs, like 2 and 24).\n", 37 | "- 49 would be a valid next number, as it is the sum of 24 and 25.\n", 38 | "- 100 would not be valid; no two of the previous 25 numbers sum to 100.\n", 39 | "- 50 would also not be valid; although 25 appears in the previous 25 numbers,\n", 40 | " the two numbers in the pair must be different.\n", 41 | "\n", 42 | "Suppose the 26th number is 45, and the first number (no longer an option, as it\n", 43 | "is more than 25 numbers ago) was 20. Now, for the next number to be valid, there\n", 44 | "needs to be some pair of numbers among 1-19, 21-25, or 45 that add up to it:\n", 45 | "\n", 46 | "- 26 would still be a valid next number, as 1 and 25 are still within the\n", 47 | " previous 25 numbers.\n", 48 | "- 65 would not be valid, as no two of the available numbers sum to it.\n", 49 | "- 64 and 66 would both be valid, as they are the result of 19+45 and 21+45\n", 50 | " respectively.\n", 51 | "\n", 52 | "Here is a larger example which only considers the previous 5 numbers (and has a\n", 53 | "preamble of length 5):\n", 54 | "\n", 55 | "```\n", 56 | "35\n", 57 | "20\n", 58 | "15\n", 59 | "25\n", 60 | "47\n", 61 | "40\n", 62 | "62\n", 63 | "55\n", 64 | "65\n", 65 | "95\n", 66 | "102\n", 67 | "117\n", 68 | "150\n", 69 | "182\n", 70 | "127\n", 71 | "219\n", 72 | "299\n", 73 | "277\n", 74 | "309\n", 75 | "576\n", 76 | "```\n", 77 | "\n", 78 | "In this example, after the 5-number preamble, almost every number is the sum of\n", 79 | "two of the previous 5 numbers; the only number that does not follow this rule\n", 80 | "is 127.\n", 81 | "\n", 82 | "The first step of attacking the weakness in the XMAS data is to find the first\n", 83 | "number in the list (after the preamble) which is not the sum of two of the 25\n", 84 | "numbers before it. **What is the first number that does not have this\n", 85 | "property?**\n" 86 | ] 87 | }, 88 | { 89 | "cell_type": "markdown", 90 | "metadata": {}, 91 | "source": [ 92 | "## Load and Clean Input data\n" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | ":::{note} The input data can be found\n", 100 | "[here](https://adventofcode.com/2020/day/9). :::\n" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": null, 106 | "metadata": {}, 107 | "outputs": [], 108 | "source": [ 109 | "with open('../../data/advent-of-code/2020/day-9-input') as fid:\n", 110 | " data = fid.readlines()\n", 111 | " data = [int(x) for x in data]\n", 112 | "\n", 113 | "print(len(data))\n", 114 | "print(data[0:2])" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": null, 120 | "metadata": {}, 121 | "outputs": [], 122 | "source": [ 123 | "test_data = [\n", 124 | " 35,\n", 125 | " 20,\n", 126 | " 15,\n", 127 | " 25,\n", 128 | " 47,\n", 129 | " 40,\n", 130 | " 62,\n", 131 | " 55,\n", 132 | " 65,\n", 133 | " 95,\n", 134 | " 102,\n", 135 | " 117,\n", 136 | " 150,\n", 137 | " 182,\n", 138 | " 127,\n", 139 | " 219,\n", 140 | " 299,\n", 141 | " 277,\n", 142 | " 309,\n", 143 | " 576,\n", 144 | "]" 145 | ] 146 | }, 147 | { 148 | "cell_type": "markdown", 149 | "metadata": {}, 150 | "source": [ 151 | "### Solution\n" 152 | ] 153 | }, 154 | { 155 | "cell_type": "code", 156 | "execution_count": 3, 157 | "metadata": {}, 158 | "outputs": [], 159 | "source": [ 160 | "def pairs_addup(data, target):\n", 161 | " n1, n2 = None, None\n", 162 | " for idx, num in enumerate(data):\n", 163 | " remain = target - num\n", 164 | " if remain in data[:idx] + data[idx + 1 :]:\n", 165 | " n1, n2 = num, remain\n", 166 | " break\n", 167 | " return n1, n2\n", 168 | "\n", 169 | "\n", 170 | "def find_invalid_number(data, preamble_length, return_index=False):\n", 171 | " arr = data[preamble_length:]\n", 172 | " for idx, num in enumerate(arr):\n", 173 | " start, stop = idx, idx + preamble_length\n", 174 | " arr_to_check = data[start:stop]\n", 175 | " n1, n2 = pairs_addup(arr_to_check, num)\n", 176 | " if n1 is None:\n", 177 | " if return_index:\n", 178 | " return num, stop\n", 179 | " else:\n", 180 | " return num\n", 181 | " return None" 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "execution_count": 4, 187 | "metadata": {}, 188 | "outputs": [ 189 | { 190 | "name": "stdout", 191 | "output_type": "stream", 192 | "text": [ 193 | "127 is the number that does not have the required property\n" 194 | ] 195 | } 196 | ], 197 | "source": [ 198 | "print(f'{find_invalid_number(test_data, 5)} is the number that does not have the required property')" 199 | ] 200 | }, 201 | { 202 | "cell_type": "code", 203 | "execution_count": 5, 204 | "metadata": {}, 205 | "outputs": [ 206 | { 207 | "name": "stdout", 208 | "output_type": "stream", 209 | "text": [ 210 | "25918798 is the number that does not have the required property\n" 211 | ] 212 | } 213 | ], 214 | "source": [ 215 | "print(f'{find_invalid_number(data, 25)} is the number that does not have the required property')" 216 | ] 217 | }, 218 | { 219 | "cell_type": "markdown", 220 | "metadata": {}, 221 | "source": [ 222 | "## Part Two\n", 223 | "\n", 224 | "The final step in breaking the XMAS encryption relies on the invalid number you\n", 225 | "just found: you must find a contiguous set of at least two numbers in your list\n", 226 | "which sum to the invalid number from step 1.\n", 227 | "\n", 228 | "Again consider the above example:\n", 229 | "\n", 230 | "```\n", 231 | "35\n", 232 | "20\n", 233 | "15\n", 234 | "25\n", 235 | "47\n", 236 | "40\n", 237 | "62\n", 238 | "55\n", 239 | "65\n", 240 | "95\n", 241 | "102\n", 242 | "117\n", 243 | "150\n", 244 | "182\n", 245 | "127\n", 246 | "219\n", 247 | "299\n", 248 | "277\n", 249 | "309\n", 250 | "576\n", 251 | "```\n", 252 | "\n", 253 | "In this list, adding up all of the numbers from 15 through 40 produces the\n", 254 | "invalid number from step 1, 127. (Of course, the contiguous set of numbers in\n", 255 | "your actual list might be much longer.)\n", 256 | "\n", 257 | "To find the encryption weakness, add together the smallest and largest number in\n", 258 | "this contiguous range; in this example, these are 15 and 47, producing 62.\n", 259 | "\n", 260 | "**What is the encryption weakness in your XMAS-encrypted list of numbers?**\n" 261 | ] 262 | }, 263 | { 264 | "cell_type": "markdown", 265 | "metadata": {}, 266 | "source": [ 267 | "### Solution\n" 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": 6, 273 | "metadata": {}, 274 | "outputs": [ 275 | { 276 | "data": { 277 | "text/plain": [ 278 | "25918798" 279 | ] 280 | }, 281 | "execution_count": 6, 282 | "metadata": {}, 283 | "output_type": "execute_result" 284 | } 285 | ], 286 | "source": [ 287 | "num = find_invalid_number(data, 25)\n", 288 | "num" 289 | ] 290 | }, 291 | { 292 | "cell_type": "code", 293 | "execution_count": 7, 294 | "metadata": {}, 295 | "outputs": [], 296 | "source": [ 297 | "def find_contiguous_block(data, target):\n", 298 | " count = 0\n", 299 | " length = len(data)\n", 300 | " # Check each size from second index all the way up to the end of the array\n", 301 | " for idx in range(2, length):\n", 302 | " # At each size, shift the window from front to back\n", 303 | " for i in range(0, length - idx + 1):\n", 304 | " total = 0\n", 305 | " candidates = []\n", 306 | " # Add numbers in the computed window and check if they sum to the target\n", 307 | " for j in range(0, idx):\n", 308 | " total += data[i + j]\n", 309 | " candidates.append(data[i + j])\n", 310 | "\n", 311 | " # Does the total equal to the target?\n", 312 | " if total == target:\n", 313 | " min_num, max_num = min(candidates), max(candidates)\n", 314 | " return min_num, max_num" 315 | ] 316 | }, 317 | { 318 | "cell_type": "code", 319 | "execution_count": 8, 320 | "metadata": {}, 321 | "outputs": [ 322 | { 323 | "data": { 324 | "text/plain": [ 325 | "(15, 47)" 326 | ] 327 | }, 328 | "execution_count": 8, 329 | "metadata": {}, 330 | "output_type": "execute_result" 331 | } 332 | ], 333 | "source": [ 334 | "find_contiguous_block(test_data, 127)" 335 | ] 336 | }, 337 | { 338 | "cell_type": "code", 339 | "execution_count": 9, 340 | "metadata": {}, 341 | "outputs": [ 342 | { 343 | "data": { 344 | "text/plain": [ 345 | "(1127699, 2213243)" 346 | ] 347 | }, 348 | "execution_count": 9, 349 | "metadata": {}, 350 | "output_type": "execute_result" 351 | } 352 | ], 353 | "source": [ 354 | "min_num, max_num = find_contiguous_block(data, num)\n", 355 | "min_num, max_num" 356 | ] 357 | }, 358 | { 359 | "cell_type": "code", 360 | "execution_count": 10, 361 | "metadata": {}, 362 | "outputs": [ 363 | { 364 | "name": "stdout", 365 | "output_type": "stream", 366 | "text": [ 367 | "The the encryption weakness in our XMAS-encrypted list of numbers is 3340942\n" 368 | ] 369 | } 370 | ], 371 | "source": [ 372 | "print(f'The the encryption weakness in our XMAS-encrypted list of numbers is {min_num + max_num}')" 373 | ] 374 | } 375 | ], 376 | "metadata": { 377 | "author": "Anderson Banihirwe", 378 | "date": "2020-12-09", 379 | "kernelspec": { 380 | "display_name": "Python 3", 381 | "language": "python", 382 | "name": "python3" 383 | }, 384 | "language_info": { 385 | "codemirror_mode": { 386 | "name": "ipython", 387 | "version": 3 388 | }, 389 | "file_extension": ".py", 390 | "mimetype": "text/x-python", 391 | "name": "python", 392 | "nbconvert_exporter": "python", 393 | "pygments_lexer": "ipython3", 394 | "version": "3.8.8" 395 | }, 396 | "tags": "python,adventofcode", 397 | "title": "Advent of Code - Day 9: Encoding Error", 398 | "widgets": { 399 | "application/vnd.jupyter.widget-state+json": { 400 | "state": {}, 401 | "version_major": 2, 402 | "version_minor": 0 403 | } 404 | } 405 | }, 406 | "nbformat": 4, 407 | "nbformat_minor": 4 408 | } 409 | -------------------------------------------------------------------------------- /posts/2020/advent-of-code-day-3.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Advent of Code - Day 3: Toboggan Trajectory\n" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## Part One\n", 15 | "\n", 16 | "With the toboggan login problems resolved, you set off toward the airport. While\n", 17 | "travel by toboggan might be easy, it's certainly not safe: there's very minimal\n", 18 | "steering and the area is covered in trees. You'll need to see which angles will\n", 19 | "take you near the fewest trees.\n", 20 | "\n", 21 | "Due to the local geology, trees in this area only grow on exact integer\n", 22 | "coordinates in a grid. You make a map (your puzzle input) of the open squares\n", 23 | "(.) and trees (#) you can see. For example:\n", 24 | "\n", 25 | "```\n", 26 | "..##.......\n", 27 | "#...#...#..\n", 28 | ".#....#..#.\n", 29 | "..#.#...#.#\n", 30 | ".#...##..#.\n", 31 | "..#.##.....\n", 32 | ".#.#.#....#\n", 33 | ".#........#\n", 34 | "#.##...#...\n", 35 | "#...##....#\n", 36 | ".#..#...#.#\n", 37 | "```\n", 38 | "\n", 39 | "These aren't the only trees, though; due to something you read about once\n", 40 | "involving arboreal genetics and biome stability, the same pattern repeats to the\n", 41 | "right many times:\n", 42 | "\n", 43 | "```\n", 44 | "..##.........##.........##.........##.........##.........##....... --->\n", 45 | "#...#...#..#...#...#..#...#...#..#...#...#..#...#...#..#...#...#..\n", 46 | ".#....#..#..#....#..#..#....#..#..#....#..#..#....#..#..#....#..#.\n", 47 | "..#.#...#.#..#.#...#.#..#.#...#.#..#.#...#.#..#.#...#.#..#.#...#.#\n", 48 | ".#...##..#..#...##..#..#...##..#..#...##..#..#...##..#..#...##..#.\n", 49 | "..#.##.......#.##.......#.##.......#.##.......#.##.......#.##..... --->\n", 50 | ".#.#.#....#.#.#.#....#.#.#.#....#.#.#.#....#.#.#.#....#.#.#.#....#\n", 51 | ".#........#.#........#.#........#.#........#.#........#.#........#\n", 52 | "#.##...#...#.##...#...#.##...#...#.##...#...#.##...#...#.##...#...\n", 53 | "#...##....##...##....##...##....##...##....##...##....##...##....#\n", 54 | ".#..#...#.#.#..#...#.#.#..#...#.#.#..#...#.#.#..#...#.#.#..#...#.# --->\n", 55 | "```\n", 56 | "\n", 57 | "You start on the open square (.) in the top-left corner and need to reach the\n", 58 | "bottom (below the bottom-most row on your map).\n", 59 | "\n", 60 | "The toboggan can only follow a few specific slopes (you opted for a cheaper\n", 61 | "model that prefers rational numbers); start by counting all the trees you would\n", 62 | "encounter for the slope right 3, down 1:\n", 63 | "\n", 64 | "From your starting position at the top-left, check the position that is right 3\n", 65 | "and down 1. Then, check the position that is right 3 and down 1 from there, and\n", 66 | "so on until you go past the bottom of the map.\n", 67 | "\n", 68 | "The locations you'd check in the above example are marked here with O where\n", 69 | "there was an open square and X where there was a tree:\n", 70 | "\n", 71 | "```\n", 72 | "..##.........##.........##.........##.........##.........##....... --->\n", 73 | "#..O#...#..#...#...#..#...#...#..#...#...#..#...#...#..#...#...#..\n", 74 | ".#....X..#..#....#..#..#....#..#..#....#..#..#....#..#..#....#..#.\n", 75 | "..#.#...#O#..#.#...#.#..#.#...#.#..#.#...#.#..#.#...#.#..#.#...#.#\n", 76 | ".#...##..#..X...##..#..#...##..#..#...##..#..#...##..#..#...##..#.\n", 77 | "..#.##.......#.X#.......#.##.......#.##.......#.##.......#.##..... --->\n", 78 | ".#.#.#....#.#.#.#.O..#.#.#.#....#.#.#.#....#.#.#.#....#.#.#.#....#\n", 79 | ".#........#.#........X.#........#.#........#.#........#.#........#\n", 80 | "#.##...#...#.##...#...#.X#...#...#.##...#...#.##...#...#.##...#...\n", 81 | "#...##....##...##....##...#X....##...##....##...##....##...##....#\n", 82 | ".#..#...#.#.#..#...#.#.#..#...X.#.#..#...#.#.#..#...#.#.#..#...#.# --->\n", 83 | "```\n", 84 | "\n", 85 | "In this example, traversing the map using this slope would cause you to\n", 86 | "encounter 7 trees.\n", 87 | "\n", 88 | "Starting at the top-left corner of your map and following a slope of right 3 and\n", 89 | "down 1, **how many trees would you encounter?**\n" 90 | ] 91 | }, 92 | { 93 | "cell_type": "markdown", 94 | "metadata": {}, 95 | "source": [ 96 | "### Load Input data\n" 97 | ] 98 | }, 99 | { 100 | "cell_type": "markdown", 101 | "metadata": {}, 102 | "source": [ 103 | ":::{note} The input data can be found\n", 104 | "[here](https://adventofcode.com/2020/day/3). :::\n" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 1, 110 | "metadata": {}, 111 | "outputs": [ 112 | { 113 | "name": "stdout", 114 | "output_type": "stream", 115 | "text": [ 116 | "323\n", 117 | "['.', '.', '.', '.', '.', '.', '.', '.', '#', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '#', '.', '.', '.', '.', '.', '.', '.', '.']\n" 118 | ] 119 | } 120 | ], 121 | "source": [ 122 | "with open('../../data/advent-of-code/2020/day-3-input') as fid:\n", 123 | " data = fid.readlines()\n", 124 | " data = [list(x.strip()) for x in data if x != '\\n']\n", 125 | "\n", 126 | "print(len(data))\n", 127 | "print(data[0])" 128 | ] 129 | }, 130 | { 131 | "cell_type": "markdown", 132 | "metadata": {}, 133 | "source": [ 134 | "### Solution\n" 135 | ] 136 | }, 137 | { 138 | "cell_type": "code", 139 | "execution_count": 2, 140 | "metadata": {}, 141 | "outputs": [], 142 | "source": [ 143 | "def tree_counter(terrain, slope, tree='#'):\n", 144 | " \"\"\"Count how many trees we encounter\"\"\"\n", 145 | " right_step, down_step = slope\n", 146 | " tree_count, idx = 0, 0\n", 147 | " for row in terrain[down_step::down_step]:\n", 148 | " idx += right_step\n", 149 | " indexer = idx % len(row)\n", 150 | " if row[indexer] == tree:\n", 151 | " tree_count += 1\n", 152 | " return tree_count" 153 | ] 154 | }, 155 | { 156 | "cell_type": "markdown", 157 | "metadata": {}, 158 | "source": [ 159 | "Let's create a small test sample to use for validating the solution above:\n" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": 3, 165 | "metadata": {}, 166 | "outputs": [], 167 | "source": [ 168 | "test_data = \"\"\"..##.........##.........##.........##.........##.........##.......\n", 169 | "#...#...#..#...#...#..#...#...#..#...#...#..#...#...#..#...#...#..\n", 170 | ".#....#..#..#....#..#..#....#..#..#....#..#..#....#..#..#....#..#.\n", 171 | "..#.#...#.#..#.#...#.#..#.#...#.#..#.#...#.#..#.#...#.#..#.#...#.#\n", 172 | ".#...##..#..#...##..#..#...##..#..#...##..#..#...##..#..#...##..#.\n", 173 | "..#.##.......#.##.......#.##.......#.##.......#.##.......#.##.....\n", 174 | ".#.#.#....#.#.#.#....#.#.#.#....#.#.#.#....#.#.#.#....#.#.#.#....#\n", 175 | ".#........#.#........#.#........#.#........#.#........#.#........#\n", 176 | "#.##...#...#.##...#...#.##...#...#.##...#...#.##...#...#.##...#...\n", 177 | "#...##....##...##....##...##....##...##....##...##....##...##....#\n", 178 | ".#..#...#.#.#..#...#.#.#..#...#.#.#..#...#.#.#..#...#.#.#..#...#.#\"\"\".splitlines()\n", 179 | "\n", 180 | "test_data = [list(line) for line in test_data]" 181 | ] 182 | }, 183 | { 184 | "cell_type": "markdown", 185 | "metadata": {}, 186 | "source": [ 187 | "Running the `tree_counter` function with a slope of `(3, 1)` produces the right\n", 188 | "output:\n" 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": 4, 194 | "metadata": {}, 195 | "outputs": [ 196 | { 197 | "data": { 198 | "text/plain": [ 199 | "7" 200 | ] 201 | }, 202 | "execution_count": 4, 203 | "metadata": {}, 204 | "output_type": "execute_result" 205 | } 206 | ], 207 | "source": [ 208 | "tree_counter(test_data, slope=(3, 1))" 209 | ] 210 | }, 211 | { 212 | "cell_type": "markdown", 213 | "metadata": {}, 214 | "source": [ 215 | "Now that we are somewhat confident about the validity of our solution, let's use\n", 216 | "our full input data to count how many trees we will encounter:\n" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": 5, 222 | "metadata": {}, 223 | "outputs": [ 224 | { 225 | "name": "stdout", 226 | "output_type": "stream", 227 | "text": [ 228 | "Found 148 Trees\n" 229 | ] 230 | } 231 | ], 232 | "source": [ 233 | "print(f'Found {tree_counter(data, slope=(3, 1))} Trees')" 234 | ] 235 | }, 236 | { 237 | "cell_type": "markdown", 238 | "metadata": {}, 239 | "source": [ 240 | "## Part Two\n", 241 | "\n", 242 | "Time to check the rest of the slopes - you need to minimize the probability of a\n", 243 | "sudden arboreal stop, after all.\n", 244 | "\n", 245 | "Determine the number of trees you would encounter if, for each of the following\n", 246 | "slopes, you start at the top-left corner and traverse the map all the way to the\n", 247 | "bottom:\n", 248 | "\n", 249 | "- Right 1, down 1.\n", 250 | "- Right 3, down 1. (This is the slope you already checked.)\n", 251 | "- Right 5, down 1.\n", 252 | "- Right 7, down 1.\n", 253 | "- Right 1, down 2.\n", 254 | "\n", 255 | "In the above example, these slopes would find 2, 7, 3, 4, and 2 tree(s)\n", 256 | "respectively; multiplied together, these produce the answer 336.\n", 257 | "\n", 258 | "**What do you get if you multiply together the number of trees encountered on\n", 259 | "each of the listed slopes?**\n" 260 | ] 261 | }, 262 | { 263 | "cell_type": "markdown", 264 | "metadata": {}, 265 | "source": [ 266 | "### Solution\n", 267 | "\n", 268 | "For part two, all we have to do is compute the number of trees for each slope,\n", 269 | "and use the `math.prod` function to multiply together the number of trees found.\n", 270 | "Here's a code cell that demonstrates that this logic works as expected when\n", 271 | "using the test sample data:\n" 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "execution_count": 6, 277 | "metadata": {}, 278 | "outputs": [ 279 | { 280 | "data": { 281 | "text/plain": [ 282 | "([2, 7, 3, 4, 2], 336)" 283 | ] 284 | }, 285 | "execution_count": 6, 286 | "metadata": {}, 287 | "output_type": "execute_result" 288 | } 289 | ], 290 | "source": [ 291 | "import math\n", 292 | "\n", 293 | "slopes = [(1, 1), (3, 1), (5, 1), (7, 1), (1, 2)]\n", 294 | "counts = [tree_counter(test_data, slope) for slope in slopes]\n", 295 | "counts, math.prod(counts)" 296 | ] 297 | }, 298 | { 299 | "cell_type": "markdown", 300 | "metadata": {}, 301 | "source": [ 302 | "It's now time to get the value for the full input data:\n" 303 | ] 304 | }, 305 | { 306 | "cell_type": "code", 307 | "execution_count": 7, 308 | "metadata": {}, 309 | "outputs": [ 310 | { 311 | "name": "stdout", 312 | "output_type": "stream", 313 | "text": [ 314 | "Counts for the given slopes [(1, 1), (3, 1), (5, 1), (7, 1), (1, 2)]: \n", 315 | "\t\t\t\t[50, 148, 53, 64, 29]\n", 316 | "The total number of trees encountered: 727923200\n" 317 | ] 318 | } 319 | ], 320 | "source": [ 321 | "counts = [tree_counter(data, slope) for slope in slopes]\n", 322 | "counts, math.prod(counts)\n", 323 | "\n", 324 | "print(\n", 325 | " f'Counts for the given slopes {slopes}: \\n\\t\\t\\t\\t{counts}\\nThe total number of trees encountered: {math.prod(counts)}'\n", 326 | ")" 327 | ] 328 | } 329 | ], 330 | "metadata": { 331 | "author": "Anderson Banihirwe", 332 | "date": "2020-12-03", 333 | "kernelspec": { 334 | "display_name": "Python 3", 335 | "language": "python", 336 | "name": "python3" 337 | }, 338 | "language_info": { 339 | "codemirror_mode": { 340 | "name": "ipython", 341 | "version": 3 342 | }, 343 | "file_extension": ".py", 344 | "mimetype": "text/x-python", 345 | "name": "python", 346 | "nbconvert_exporter": "python", 347 | "pygments_lexer": "ipython3", 348 | "version": "3.8.8" 349 | }, 350 | "tags": "python,adventofcode", 351 | "title": "Advent of Code - Day 3: Toboggan Trajectory", 352 | "widgets": { 353 | "application/vnd.jupyter.widget-state+json": { 354 | "state": {}, 355 | "version_major": 2, 356 | "version_minor": 0 357 | } 358 | } 359 | }, 360 | "nbformat": 4, 361 | "nbformat_minor": 4 362 | } 363 | -------------------------------------------------------------------------------- /posts/2020/advent-of-code-day-2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Advent of Code - Day 2: Password Philosophy\n" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## Part One\n", 15 | "\n", 16 | "Your flight departs in a few days from the coastal airport; the easiest way down\n", 17 | "to the coast from here is via\n", 18 | "[toboggan](https://en.wikipedia.org/wiki/Toboggan).\n", 19 | "\n", 20 | "The shopkeeper at the North Pole Toboggan Rental Shop is having a bad day.\n", 21 | "\"Something's wrong with our computers; we can't log in!\" You ask if you can take\n", 22 | "a look.\n", 23 | "\n", 24 | "Their password database seems to be a little corrupted: some of the passwords\n", 25 | "wouldn't have been allowed by the Official Toboggan Corporate Policy that was in\n", 26 | "effect when they were chosen.\n", 27 | "\n", 28 | "To try to debug the problem, they have created a list (your puzzle input) of\n", 29 | "passwords (according to the corrupted database) and the corporate policy when\n", 30 | "that password was set.\n", 31 | "\n", 32 | "For example, suppose you have the following list:\n", 33 | "\n", 34 | "- 1-3 a: abcde\n", 35 | "- 1-3 b: cdefg\n", 36 | "- 2-9 c: ccccccccc\n", 37 | "\n", 38 | "Each line gives the password policy and then the password. The password policy\n", 39 | "indicates the lowest and highest number of times a given letter must appear for\n", 40 | "the password to be valid. For example, 1-3 a means that the password must\n", 41 | "contain a at least 1 time and at most 3 times.\n", 42 | "\n", 43 | "In the above example, 2 passwords are valid. The middle password, cdefg, is not;\n", 44 | "it contains no instances of b, but needs at least 1. The first and third\n", 45 | "passwords are valid: they contain one a or nine c, both within the limits of\n", 46 | "their respective policies.\n", 47 | "\n", 48 | "How many passwords are valid according to their policies?\n" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "metadata": {}, 54 | "source": [ 55 | "### Load Input data\n" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | ":::{note} The input data can be found\n", 63 | "[here](https://adventofcode.com/2020/day/2). :::\n" 64 | ] 65 | }, 66 | { 67 | "cell_type": "code", 68 | "execution_count": 1, 69 | "metadata": {}, 70 | "outputs": [], 71 | "source": [ 72 | "with open('../../data/advent-of-code/2020/day-2-input') as fid:\n", 73 | " data = fid.readlines()\n", 74 | " data = [x.strip() for x in data if x != '\\n']" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": 2, 80 | "metadata": {}, 81 | "outputs": [ 82 | { 83 | "data": { 84 | "text/plain": [ 85 | "(['6-7 z: dqzzzjbzz',\n", 86 | " '13-16 j: jjjvjmjjkjjjjjjj',\n", 87 | " '5-6 m: mmbmmlvmbmmgmmf',\n", 88 | " '2-4 k: pkkl'],\n", 89 | " 1000)" 90 | ] 91 | }, 92 | "execution_count": 2, 93 | "metadata": {}, 94 | "output_type": "execute_result" 95 | } 96 | ], 97 | "source": [ 98 | "data[:4], len(data)" 99 | ] 100 | }, 101 | { 102 | "cell_type": "markdown", 103 | "metadata": {}, 104 | "source": [ 105 | "### Solution\n", 106 | "\n", 107 | "My solution consists of a single function that parses and validates a single\n", 108 | "password and returns `True` when the password is valid.\n" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": 3, 114 | "metadata": {}, 115 | "outputs": [], 116 | "source": [ 117 | "import collections" 118 | ] 119 | }, 120 | { 121 | "cell_type": "code", 122 | "execution_count": 4, 123 | "metadata": {}, 124 | "outputs": [], 125 | "source": [ 126 | "def is_valid_password(entry):\n", 127 | " \"\"\"\n", 128 | " Checks whether the password is valid according to the policy.\n", 129 | " \"\"\"\n", 130 | " required_count, letter, password = entry.split()\n", 131 | " letter = letter.split(':')[0]\n", 132 | " min_req_count, max_req_count = required_count.split('-')\n", 133 | " min_req_count, max_req_count = int(min_req_count), int(max_req_count)\n", 134 | " count = collections.Counter(password) # Get a count for each letter in the password\n", 135 | " return min_req_count <= count[letter] <= max_req_count" 136 | ] 137 | }, 138 | { 139 | "cell_type": "markdown", 140 | "metadata": {}, 141 | "source": [ 142 | "Let's validate the password in the example so as to make sure this function is\n", 143 | "working properly:\n" 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "execution_count": 5, 149 | "metadata": {}, 150 | "outputs": [ 151 | { 152 | "data": { 153 | "text/plain": [ 154 | "True" 155 | ] 156 | }, 157 | "execution_count": 5, 158 | "metadata": {}, 159 | "output_type": "execute_result" 160 | } 161 | ], 162 | "source": [ 163 | "is_valid_password('1-3 a: abcde')" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": 6, 169 | "metadata": {}, 170 | "outputs": [ 171 | { 172 | "data": { 173 | "text/plain": [ 174 | "False" 175 | ] 176 | }, 177 | "execution_count": 6, 178 | "metadata": {}, 179 | "output_type": "execute_result" 180 | } 181 | ], 182 | "source": [ 183 | "is_valid_password('1-3 b: cdefg')" 184 | ] 185 | }, 186 | { 187 | "cell_type": "code", 188 | "execution_count": 7, 189 | "metadata": {}, 190 | "outputs": [ 191 | { 192 | "data": { 193 | "text/plain": [ 194 | "True" 195 | ] 196 | }, 197 | "execution_count": 7, 198 | "metadata": {}, 199 | "output_type": "execute_result" 200 | } 201 | ], 202 | "source": [ 203 | "is_valid_password('2-9 c: ccccccccc')" 204 | ] 205 | }, 206 | { 207 | "cell_type": "markdown", 208 | "metadata": {}, 209 | "source": [ 210 | "Now that we are confident this function is working properly, let's loop over all\n", 211 | "entries in the input dataset, and count how many passwords are valid:\n" 212 | ] 213 | }, 214 | { 215 | "cell_type": "code", 216 | "execution_count": 8, 217 | "metadata": {}, 218 | "outputs": [], 219 | "source": [ 220 | "valid_passwords = 0\n", 221 | "for entry in data:\n", 222 | " if is_valid_password(entry):\n", 223 | " valid_passwords += 1" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": 9, 229 | "metadata": {}, 230 | "outputs": [ 231 | { 232 | "data": { 233 | "text/plain": [ 234 | "542" 235 | ] 236 | }, 237 | "execution_count": 9, 238 | "metadata": {}, 239 | "output_type": "execute_result" 240 | } 241 | ], 242 | "source": [ 243 | "valid_passwords" 244 | ] 245 | }, 246 | { 247 | "cell_type": "code", 248 | "execution_count": 10, 249 | "metadata": {}, 250 | "outputs": [ 251 | { 252 | "name": "stdout", 253 | "output_type": "stream", 254 | "text": [ 255 | "Out of 1000 passwords, 542 are found to be valid.\n" 256 | ] 257 | } 258 | ], 259 | "source": [ 260 | "print(f'Out of {len(data)} passwords, {valid_passwords} are found to be valid.')" 261 | ] 262 | }, 263 | { 264 | "cell_type": "markdown", 265 | "metadata": {}, 266 | "source": [ 267 | "## Part Two\n", 268 | "\n", 269 | "While it appears you validated the passwords correctly, they don't seem to be\n", 270 | "what the Official Toboggan Corporate Authentication System is expecting.\n", 271 | "\n", 272 | "The shopkeeper suddenly realizes that he just accidentally explained the\n", 273 | "password policy rules from his old job at the sled rental place down the street!\n", 274 | "The Official Toboggan Corporate Policy actually works a little differently.\n", 275 | "\n", 276 | "Each policy actually describes two positions in the password, where 1 means the\n", 277 | "first character, 2 means the second character, and so on. (Be careful; Toboggan\n", 278 | "Corporate Policies have no concept of \"index zero\"!) **Exactly one of these\n", 279 | "positions must contain the given letter**. Other occurrences of the letter are\n", 280 | "irrelevant for the purposes of policy enforcement.\n", 281 | "\n", 282 | "Given the same example list from above:\n", 283 | "\n", 284 | "- 1-3 a: abcde is valid: position 1 contains a and position 3 does not.\n", 285 | "- 1-3 b: cdefg is invalid: neither position 1 nor position 3 contains b.\n", 286 | "- 2-9 c: ccccccccc is invalid: both position 2 and position 9 contain c.\n", 287 | "\n", 288 | "**How many passwords are valid according to the new interpretation of the\n", 289 | "policies?**\n" 290 | ] 291 | }, 292 | { 293 | "cell_type": "markdown", 294 | "metadata": {}, 295 | "source": [ 296 | "### Solution\n", 297 | "\n", 298 | "My approach to part two is similar to my solution to part one in that I still\n", 299 | "have a single function for validating a single entry. In this function, I use a\n", 300 | "[bitwise XOR operator](https://en.wikipedia.org/wiki/Bitwise_operation#XOR_2).\n", 301 | "The Bitwise XOR sets the bits in the result to 1 if either, but not both, of the\n", 302 | "corresponding bits in the two operands is 1:\n" 303 | ] 304 | }, 305 | { 306 | "cell_type": "code", 307 | "execution_count": 11, 308 | "metadata": {}, 309 | "outputs": [], 310 | "source": [ 311 | "def is_valid_password(entry):\n", 312 | " required_positions, letter, password = entry.split()\n", 313 | " letter = letter.split(':')[0]\n", 314 | " first_position, second_position = required_positions.split('-')\n", 315 | " first_position, second_position = int(first_position), int(second_position)\n", 316 | " # use bitwise XOR to make sure exactly one of the positions contains the given letter\n", 317 | " if (password[first_position - 1] == letter) ^ (password[second_position - 1] == letter):\n", 318 | " return True\n", 319 | " return False" 320 | ] 321 | }, 322 | { 323 | "cell_type": "markdown", 324 | "metadata": {}, 325 | "source": [ 326 | "Once again, let's make sure this function is working properly by using the three\n", 327 | "sample examples above:\n" 328 | ] 329 | }, 330 | { 331 | "cell_type": "code", 332 | "execution_count": 12, 333 | "metadata": {}, 334 | "outputs": [ 335 | { 336 | "data": { 337 | "text/plain": [ 338 | "True" 339 | ] 340 | }, 341 | "execution_count": 12, 342 | "metadata": {}, 343 | "output_type": "execute_result" 344 | } 345 | ], 346 | "source": [ 347 | "is_valid_password('1-3 a: abcde')" 348 | ] 349 | }, 350 | { 351 | "cell_type": "code", 352 | "execution_count": 13, 353 | "metadata": {}, 354 | "outputs": [ 355 | { 356 | "data": { 357 | "text/plain": [ 358 | "False" 359 | ] 360 | }, 361 | "execution_count": 13, 362 | "metadata": {}, 363 | "output_type": "execute_result" 364 | } 365 | ], 366 | "source": [ 367 | "is_valid_password('1-3 b: cdefg')" 368 | ] 369 | }, 370 | { 371 | "cell_type": "code", 372 | "execution_count": 14, 373 | "metadata": {}, 374 | "outputs": [ 375 | { 376 | "data": { 377 | "text/plain": [ 378 | "False" 379 | ] 380 | }, 381 | "execution_count": 14, 382 | "metadata": {}, 383 | "output_type": "execute_result" 384 | } 385 | ], 386 | "source": [ 387 | "is_valid_password('2-9 c: ccccccccc')" 388 | ] 389 | }, 390 | { 391 | "cell_type": "markdown", 392 | "metadata": {}, 393 | "source": [ 394 | "It appears that this function is working.... It's now time to count all valid\n", 395 | "passwords from our input data:\n" 396 | ] 397 | }, 398 | { 399 | "cell_type": "code", 400 | "execution_count": 15, 401 | "metadata": {}, 402 | "outputs": [ 403 | { 404 | "name": "stdout", 405 | "output_type": "stream", 406 | "text": [ 407 | "Out of 1000 passwords, 360 are found to be valid.\n" 408 | ] 409 | } 410 | ], 411 | "source": [ 412 | "valid_passwords = 0\n", 413 | "for entry in data:\n", 414 | " if is_valid_password(entry):\n", 415 | " valid_passwords += 1\n", 416 | "\n", 417 | "print(f'Out of {len(data)} passwords, {valid_passwords} are found to be valid.')" 418 | ] 419 | } 420 | ], 421 | "metadata": { 422 | "author": "Anderson Banihirwe", 423 | "date": "2020-12-02", 424 | "kernelspec": { 425 | "display_name": "Python 3", 426 | "language": "python", 427 | "name": "python3" 428 | }, 429 | "language_info": { 430 | "codemirror_mode": { 431 | "name": "ipython", 432 | "version": 3 433 | }, 434 | "file_extension": ".py", 435 | "mimetype": "text/x-python", 436 | "name": "python", 437 | "nbconvert_exporter": "python", 438 | "pygments_lexer": "ipython3", 439 | "version": "3.8.8" 440 | }, 441 | "tags": "python,adventofcode", 442 | "title": "Advent of Code - Day 2: Password Philosophy", 443 | "widgets": { 444 | "application/vnd.jupyter.widget-state+json": { 445 | "state": {}, 446 | "version_major": 2, 447 | "version_minor": 0 448 | } 449 | } 450 | }, 451 | "nbformat": 4, 452 | "nbformat_minor": 4 453 | } 454 | -------------------------------------------------------------------------------- /data/advent-of-code/2020/day-5-input: -------------------------------------------------------------------------------- 1 | FBBFFBBLLL 2 | FFBFFFBRLL 3 | FFBBBBFRRL 4 | FBFBBBBRLL 5 | BFBBBBFLLR 6 | FFFBBBBLRR 7 | BFFFFFBLLL 8 | BBFFFBFRRL 9 | FFBFFFFLLR 10 | BFFFBBBRRL 11 | FBFBFFFLRL 12 | FFFBBFBLRR 13 | FBFBFBFLRR 14 | FBBBBFBRRL 15 | BFFBFFBRRR 16 | FBBBFBBRLL 17 | FBFFBFBRLR 18 | BBFBFFFLRL 19 | FFBFFFFRLR 20 | FFBBFBFRRR 21 | BFBBBFBLRR 22 | FFBBFFFLRL 23 | FBBBBFFRLR 24 | FBBBBBBRLR 25 | FFBFBFBLLL 26 | BBFBFBBLLL 27 | FFFFFBBRRL 28 | FBFFBFBRRR 29 | FFFBFFFRLL 30 | BFBFBFFLLL 31 | BFBFFBFLLL 32 | FFFFBFFRRL 33 | FBFFFFFRLR 34 | FBBFFBBLLR 35 | BFFFFFBLRL 36 | BFBFFFBLLR 37 | FBBBBBBLLL 38 | BBFBFBFLLL 39 | FFBFFBFLRR 40 | BBFFFBBRLL 41 | FFBFFFFLLL 42 | FBBFFBFLLL 43 | FFFBBBFRRR 44 | BFBBFBFRLL 45 | FBBFBFFRRL 46 | FBFBBFBLRR 47 | FFBBBBBRLR 48 | FFBFBFFLLL 49 | FBFFFBFRRL 50 | BFFBBFFRLL 51 | BFFBBFFLLR 52 | BBFFBFFRRL 53 | FBFBFBFLLR 54 | BBFFBFBLRL 55 | BFBFBFBLRR 56 | FFBBFBBRRL 57 | BFBBBFBRLR 58 | FBFFFFBLLR 59 | BFBFFBBLRR 60 | BBFBFBFRRR 61 | FFBFBBBRRL 62 | BFBFBBFRLR 63 | FBFBBBBRLR 64 | BFFFFBBRRR 65 | BBFFFBFLLR 66 | FBFFFFBRRL 67 | BBFFBFBLLL 68 | BFFFBBBLLL 69 | FFBFFFBLRR 70 | BFBFBBBRRR 71 | FBFFBFFRRR 72 | BFFBFFBLLL 73 | BFBBFFFLRL 74 | BBFBFFBRLR 75 | FBBFFFFLLL 76 | FBFFFFBLLL 77 | BBFFFFBRLR 78 | BBFBFFBRRL 79 | FBBFBFBRRR 80 | FBFFBFFLRR 81 | FFBBBFBLLR 82 | FFFFFBFRRR 83 | BFBBBBBLLR 84 | BBFBFFFRLL 85 | BFFBFBBLRR 86 | BFFBBFBRLL 87 | FBFFBFFRLR 88 | FBFBBFFLLR 89 | FFFFFBFRRL 90 | FFBFFBFRLR 91 | FBFBBBFLLL 92 | FFBFBBFLLR 93 | FBFFBBBRLR 94 | FFFBFBFLLR 95 | BFBBFBBLRL 96 | BFFFFBFLLL 97 | BFBFFBFLRL 98 | BBFFBFBLLR 99 | BFFFBFBRRL 100 | BBFBFFBLLR 101 | FFBBFFFRLL 102 | FFFFFBBRLR 103 | FFBFBFBRLL 104 | FBBBBFBLRR 105 | FFFBFFFRRL 106 | FFFFBFFLRL 107 | FFFFBFFRLR 108 | BFFFFFFRRR 109 | BBFFBFBRRL 110 | BBFBFFFLRR 111 | FFBBBFFLLR 112 | FBFBFBBRLR 113 | FBBFBBBLRR 114 | BFFBBBFRLL 115 | BFBBFBBLLL 116 | FBFFFBBLLR 117 | FBBBBFFRRL 118 | BFBBFFFLLR 119 | BFBBBFFLRL 120 | FFBFFBBRLL 121 | FBFBFFBRRR 122 | FBBFBBBLRL 123 | BFFFBFFLRL 124 | BFFBFFBLRR 125 | BFBFBBFLRL 126 | FBFBBFFRLL 127 | BFFFFBBRRL 128 | FFBBBBBLRL 129 | FBBFFBFRRL 130 | BBFFBFBRLL 131 | FBFFFFFLRL 132 | FFFBFBFRLR 133 | FFBBBBFLLR 134 | FFFBBBFRLR 135 | FFBBFFBRRR 136 | FFFBFFBLRL 137 | FBFBFBBLLL 138 | BFFFFBBLRR 139 | BBFFBBFLLR 140 | FFFFFFBRLL 141 | FBBBBBBLRR 142 | FFFFBFBLLR 143 | BBFFBBFRRL 144 | BFBBBBBLRR 145 | BFBBFBBLRR 146 | BBFFFFBRRR 147 | BFBBBFBLLR 148 | BBFFBBBRRR 149 | FBBFBFBRLL 150 | FFFFFBBLRR 151 | BBFBFBBLLR 152 | FBFFFBFRLR 153 | BBFFBFFLLR 154 | BBFBFFFRLR 155 | FFFFBFBLRL 156 | FFBBBBBLRR 157 | FBBBFFBLRR 158 | FBBFBBFRLL 159 | FBBFBFBRLR 160 | FFBFBBBLRL 161 | FBFFBBBLLL 162 | FFFBBBBRRR 163 | FFBFFBFLRL 164 | BFFFBBFRLL 165 | BFFFFBBLLR 166 | BFFFFFBRRL 167 | BFFBFBBRRL 168 | FFBFFFBRRL 169 | BFFBFFFLRL 170 | BFFBBBFLLL 171 | FFBBFFFLRR 172 | FBBBFFBRLL 173 | FFBBFFFRLR 174 | BFBFBFFRLL 175 | FFFFBBFRRR 176 | BFBFBBFRRL 177 | FBFBBBFLRL 178 | FFFBFBBRLL 179 | FFBBBFFLLL 180 | BBFFFBBLRL 181 | FBBBFFBRRR 182 | BFBFBFBLLL 183 | FFBFFBBRLR 184 | FFBFBBBRLR 185 | FFFFFFBRLR 186 | BFFBBFFLRL 187 | FFBFFBFRRR 188 | FFBFBFFRLL 189 | FBFBFFBLLR 190 | FBFBFBBRRR 191 | FFFBBFBRRR 192 | FBBBBFBLLR 193 | FFFFBFBRLR 194 | FFFBBBFRLL 195 | FBFFBFBLLL 196 | FBFBFFFLRR 197 | FFFBBFFLLL 198 | BFBFFFFLLL 199 | BBFFFBFLLL 200 | FFFFBFBRRL 201 | FFBBBFBRRL 202 | BFBFFBBRLR 203 | FFFBBFFLRL 204 | FFFBFBBLLR 205 | FFBBBFFRLR 206 | FBBBBFBLRL 207 | BFFFBBFLLL 208 | BFFBFBFLLR 209 | FBFFBBFLRR 210 | BFFBBBBLLR 211 | FBBBFFFRRR 212 | FBFFBFBLRL 213 | BFFFFFFRLR 214 | FBBBBFBRRR 215 | FFBBBBFLRL 216 | BBFBFFBLRL 217 | BFBFBBBLRR 218 | FFFFFBFLLR 219 | FBBFFBFLLR 220 | BFFFFBBRLL 221 | FFBFBBBLRR 222 | FFFFBFFLLR 223 | BBFFFFFRLR 224 | FBBBBFFRLL 225 | BFFFBFBLRR 226 | FBFBBBBLLR 227 | BFFBFFBRLL 228 | BFBFBFFLRR 229 | BFFFFBFLRR 230 | FBBFFBFLRR 231 | BFFBBFBLLR 232 | FBBFFFBLRL 233 | FBFBFBBRRL 234 | FBFFBFFRRL 235 | BFBBFBFLLR 236 | FFFFBFFLRR 237 | BFBFFFBLRL 238 | BFFBBBBRRR 239 | FFFFBBBLRR 240 | FFFBBFBLLL 241 | FFBBFBFLLR 242 | BFBBFBFLLL 243 | FBBFBFFLLR 244 | FFFBBFFRRR 245 | FFBBBBBRRR 246 | FFFBFFFRLR 247 | BFFBFBFLRR 248 | FBFFBFBRLL 249 | FFFFBFFLLL 250 | FFBBBFFRLL 251 | FBFBFFBLLL 252 | FBFBBBFLLR 253 | BFBBBFFRLL 254 | BFBBBBBRRL 255 | FFFFFFBRRL 256 | FBBFBBFLRL 257 | BFFBFBBRLL 258 | FFFFFBFLRL 259 | BFBFFFBLRR 260 | BBFFFBFLRR 261 | BFBFBBBLRL 262 | FBFFFFBRLR 263 | BFBFBBBLLL 264 | BFFFFFFRRL 265 | FBBFFFBRRR 266 | FBBFBFFLRL 267 | BBFFFFFLRL 268 | BFFFFBBLRL 269 | FBFBBFFRRR 270 | FBBFFFFLRR 271 | FFFBFFFLRR 272 | FBFBBFFRRL 273 | BFFBFFBRRL 274 | BFFBFBFRRL 275 | FFBBFBBLLR 276 | FBFFBFFRLL 277 | BFBFFBBLLL 278 | FFBBFFBLLL 279 | BBFFFBBRRL 280 | BFFFFFFLLL 281 | FFBBBBFRLL 282 | BFBBBBFRRR 283 | BFFBBBBRLR 284 | FBFFBBFRLL 285 | BBFBFFBLRR 286 | FBFFBBBLRR 287 | BBFBFFFRRL 288 | BFBFFFFLRR 289 | BFBFFBBRRR 290 | FBBFFFBLLL 291 | BFFBBBFRLR 292 | BFFFFBFRLL 293 | FBFBBBBRRL 294 | BBFFBBFLRL 295 | FBBBBFFLLR 296 | BFBBFFBRRR 297 | FBBFFBBLRR 298 | FBBBBBBRRL 299 | BBFFFBBLLR 300 | BFBBBBFLRL 301 | BFFBFFFRLR 302 | FFFFBFBRRR 303 | FFFFBFBRLL 304 | BFFFFFFRLL 305 | BFFFBBBRLL 306 | FFFFFBBLLL 307 | FBFFFBFLRR 308 | FBFBFBFLLL 309 | BFBBFFBRLL 310 | BFFFBBFRLR 311 | BFBBFFFRLL 312 | FFFBBFBLRL 313 | BBFFFFFLRR 314 | BBFFFBFRLR 315 | BFBBBBFLRR 316 | FBFFFBBLLL 317 | BBFFBFBRRR 318 | FBFBFBBLRR 319 | FBBFBFFRLR 320 | FBFBBBBLLL 321 | FFFBBFBLLR 322 | FFFFBBBRRL 323 | FFBBBBFRLR 324 | FFFBFFBRRL 325 | BFBBFBBLLR 326 | FFBFFFFLRL 327 | BFFBFBFRLR 328 | FFBBFBBLLL 329 | BFFBBBFLRL 330 | FFFFFBFRLL 331 | BFFBFFBRLR 332 | BFFFBFFRRL 333 | BFBFBBBRLL 334 | BFFFFFBLRR 335 | FBBFFBBRLL 336 | FFFFBBBLRL 337 | BBFFFFBLRR 338 | BFBFBBBLLR 339 | BFFFBFFRLR 340 | BFBBFBFRRR 341 | BFFBBBFLRR 342 | FFBFBBFRRR 343 | FBBBFBFRLR 344 | BFBFFBBRLL 345 | BBFFBFFRLL 346 | BFFFBFBLLR 347 | FBFBBFFLRL 348 | BFFFFBBLLL 349 | BFBBFBBRRL 350 | BFFFBFFLLL 351 | BFBBBFBLLL 352 | FFBBFBFLRR 353 | FFBBFBFRLR 354 | BFBBBFFRRR 355 | BBFBFFFLLL 356 | BFFFFFBRLL 357 | FBBFBBFRRR 358 | BFFFBBFLRR 359 | BFFBBBFRRL 360 | FFBFFFFRRL 361 | FBFFBFBLRR 362 | FFBFBFBRLR 363 | BBFFBFFLRR 364 | BFFBBFFLLL 365 | FFBBBFBRRR 366 | BBFFBFFRRR 367 | FBBFFFFRRR 368 | FBBBBFFLRR 369 | FBBFBBBRRR 370 | FFBFBFBLRL 371 | FBBFFFBLRR 372 | FBFFBFFLRL 373 | BFBBFFFRRR 374 | FBFBBBFRLR 375 | BFFFFFBRRR 376 | FBFBFFFRRR 377 | BBFFFBFLRL 378 | FFBFBFBLLR 379 | FBFFFBBRRL 380 | FBBBFFBLLL 381 | BFBFBFFRRR 382 | FBFFBBBRLL 383 | FFBFFBBLLR 384 | FFBBFBBRLL 385 | BFBFBBFRLL 386 | BFBBFFBLLL 387 | BFFBBBFRRR 388 | BFBFBFBRRL 389 | BFFFFFFLLR 390 | BFFFBBBRRR 391 | FBBBFBFRRR 392 | FFFBFFBRLL 393 | BFFFFBFLRL 394 | FBFBBFFRLR 395 | FBBBBBFLRL 396 | FFFBBBFRRL 397 | BFBFFFFLLR 398 | FBFFFBBLRR 399 | FBBFBBBRRL 400 | FFFBBBFLRR 401 | BFBFFBFRRL 402 | BFFFBBFRRL 403 | FBBBFFBLLR 404 | FFBBFBBLRL 405 | BBFBFBFRLR 406 | FFBFFBFLLL 407 | FFFFBFBLLL 408 | BBFFFFBRLL 409 | FBFFFBBRLR 410 | FFBBBFFRRR 411 | FFFBFFFRRR 412 | FFBFBBBLLR 413 | FFBFFFBRLR 414 | FFBBBFBLRR 415 | BFFBBBBLRL 416 | BFFFFBFRRR 417 | BBFBFBFRLL 418 | FBFBBBFLRR 419 | FFFFFBFLLL 420 | BFBBBFFLLR 421 | BFFBFBFRRR 422 | BFBBFFBLRR 423 | BBFFFBBRLR 424 | FBBBBBBLRL 425 | BFFBBBBRRL 426 | FFBFBFFRLR 427 | BFFFFFBLLR 428 | BBFFFFBRRL 429 | FFFBBFFLRR 430 | BFFFFBFRRL 431 | BBFFBFFLRL 432 | BBFBFBFLRR 433 | FBFBBBBLRR 434 | BFFBBFBRRR 435 | FFFBBBBRLL 436 | FBFBBBFRRL 437 | FFFFFFBRRR 438 | BFFFFFFLRL 439 | FFBBFFFLLR 440 | FBBFBFBLLL 441 | FBBFBBFRLR 442 | BFFBFFBLLR 443 | FBFBBFBRLL 444 | FBFBFBFLRL 445 | FFBFFFBLLR 446 | FBBFFBBLRL 447 | FFFBBBBLLR 448 | BFFBBBBLRR 449 | BFFBFBBLLR 450 | FFBBFFBRLR 451 | FBBBBFFLLL 452 | BFFFFFBRLR 453 | BFBBBBFRLL 454 | FFFBBFFRLR 455 | BFFFBFBLLL 456 | BBFFFFFRRL 457 | FBFFFFBRLL 458 | BFBFBFBLRL 459 | FBBFBBFLRR 460 | FFBFBBFLRR 461 | FFBFBFFLRR 462 | FFBFFFFRLL 463 | BFBFBFBRRR 464 | FBBBBFFLRL 465 | FFBFFFBLLL 466 | BFBBBFFRLR 467 | FBFBFFBRLL 468 | FBBBBBFRRL 469 | FBFFBFFLLL 470 | BBFFBBBLLR 471 | BFFFBFFRRR 472 | BBFFBBBRLL 473 | BBFFFBBLRR 474 | FBBFBBBRLR 475 | BFBFBBFRRR 476 | FBFFBBFLRL 477 | FFFFBBFLRR 478 | BFFFBFBRLL 479 | BBFFFFFRLL 480 | FFFBFBFRRR 481 | BFBBBBFLLL 482 | FFBBBFBRLR 483 | FFFFBBBRRR 484 | BFBFFBFRLR 485 | FBFFBBFLLL 486 | FFBFFBBLRR 487 | FBFBFFBLRR 488 | FFBFFBBRRL 489 | FFFBBBBRRL 490 | BFFBFBBLRL 491 | BBFFBBBLRR 492 | FFFBBFBRLL 493 | FFFBFBBLRL 494 | BFBFFFBRRL 495 | FBBFFFFLLR 496 | BBFBFFBLLL 497 | BFBFBBFLLR 498 | BFFBFBBRRR 499 | FBFFFFFLLR 500 | FFBBBFFLRL 501 | FBFFBBFRLR 502 | BFFFBFBLRL 503 | FBFBBBFRRR 504 | FFBFFBFRRL 505 | FBFBBFBRLR 506 | BFBFBFFRLR 507 | FBFFBBBRRR 508 | BBFBFFFLLR 509 | FBBFBBBLLL 510 | FFBBBFBLLL 511 | FFBFFFFRRR 512 | BFFBFBFLLL 513 | BFFBBBFLLR 514 | FBBBBBBRLL 515 | FBBFFBFRLL 516 | FFFBFFBLLR 517 | FFBBFFBLRL 518 | BFBBFBFLRR 519 | BFBFFFBLLL 520 | FBFFFBBRRR 521 | FFBFBFFLLR 522 | FBFFBBBRRL 523 | FBBBBFBLLL 524 | FFFFFBBLLR 525 | FBBFBFBLRL 526 | BFBBFBBRLL 527 | FFFFBFBLRR 528 | BBFFBBFLLL 529 | FBBBFBFRLL 530 | FBFBBFBLLL 531 | BBFBFBFLLR 532 | BFBFBFBRLR 533 | FFFFFBFRLR 534 | FFFFFBBRLL 535 | FBFBFBFRRR 536 | FBBBFBFLRL 537 | BBFFBBBLRL 538 | FFBBBBBRLL 539 | FBBBFBBRRR 540 | BFFFBBBLLR 541 | BFBBBBBRLL 542 | FFFFFBBLRL 543 | BBFFBFFRLR 544 | BFBFBFBRLL 545 | FFFFBBFLLL 546 | BFBFBFFRRL 547 | FFBBFFBLLR 548 | BFBBFFFLLL 549 | BFBBFFBRLR 550 | FBFBFFBRLR 551 | FBFFFBFRLL 552 | FBBFBFFLRR 553 | FBBFFBFRRR 554 | BFBFFFFRLL 555 | BFFFBBBLRL 556 | FBFBBBBRRR 557 | BBFFBBFLRR 558 | FBBBBFBRLL 559 | BBFFBBBRRL 560 | BFBBBFFLLL 561 | FBFFFFBLRL 562 | FBFFFFFRRL 563 | BFBBBFFLRR 564 | FBBFBFBLRR 565 | FBFBFBBLRL 566 | FFBFBFFRRL 567 | BFFBBFBRLR 568 | FFBFFBBLLL 569 | FBBBFBBLLL 570 | FBBBFBBRRL 571 | FBBBFBBLLR 572 | FBFFFBFLLL 573 | FBBBFFBRLR 574 | FBBFBBBRLL 575 | FBBFFFBLLR 576 | FFFBFBBRRR 577 | FBBFFFFLRL 578 | BFFFFBFRLR 579 | FBBBFFFLLL 580 | FFBBFBFLLL 581 | BBFFFFFRRR 582 | BBFFFBFRRR 583 | FFBFFBBLRL 584 | FBBBFFBLRL 585 | FFBBFBFRRL 586 | FBFFFBFLRL 587 | FBBBFFFLRL 588 | FFFFBBBRLR 589 | FFBBFBBRLR 590 | FBFBFFBRRL 591 | BFBFFBFRRR 592 | BFBFBFFLRL 593 | BFFBFFFLLL 594 | FBBFBFBRRL 595 | FFBBBFBLRL 596 | BFFFFBFLLR 597 | FBBBFBFLLL 598 | FBFFFBBLRL 599 | FBBFFFBRRL 600 | FBBBBBFLLR 601 | FBBBFBFRRL 602 | FFFBBFBRRL 603 | FBBBFFBRRL 604 | FBFBFBBRLL 605 | BFBBBBBLLL 606 | FBFBFFFLLR 607 | FFFBFFBRLR 608 | BBFFBFBLRR 609 | BFFFBFFLLR 610 | FBBFBBFRRL 611 | FFBBBBBLLL 612 | FBBFBFFRLL 613 | FFFBBBFLRL 614 | FFBBBBFLRR 615 | FBFFFFFRLL 616 | BBFFFFBLLR 617 | FFFFBBFRRL 618 | FFBBBBFRRR 619 | FBBBFBBLRL 620 | BFFBBFBLRL 621 | BFBBFFFRLR 622 | FFFBFBBRRL 623 | BBFBFBFRRL 624 | FFFBBBFLLR 625 | BFFFFBBRLR 626 | BFFBFFFLLR 627 | BBFBFFFRRR 628 | FFFFBBFRLR 629 | BFBBBFBRRR 630 | FBFBBBBLRL 631 | FFFFBBFLLR 632 | FBFBBBFRLL 633 | BFBBFBBRLR 634 | FFBBFFFRRR 635 | FFBBBFBRLL 636 | FBBFFBFLRL 637 | BFBFFFBRRR 638 | BFFFBBBRLR 639 | FFFFBFFRRR 640 | FFBBFFBLRR 641 | FBBFBFBLLR 642 | FFFBBBFLLL 643 | FBBBBBFLLL 644 | FFFFBBBLLL 645 | FFFBFFBLLL 646 | BFBBBBBRLR 647 | BFBFBBFLRR 648 | FBFBBFBRRR 649 | BFBBFFFLRR 650 | FFBBFFFLLL 651 | BFFFBBFLLR 652 | FFBBFBFRLL 653 | FBBBFFFRLL 654 | FFFBBFBRLR 655 | BFBFFBFLRR 656 | FBFBBFBLLR 657 | FBFFFFBRRR 658 | FFFBFBBLLL 659 | FBFBFFFRRL 660 | FFFBFBFLRR 661 | FFBBBBBRRL 662 | FFFBFFFLLR 663 | BFFFBFFRLL 664 | BFBBBBBLRL 665 | FBBBFBFLRR 666 | FFFFBBBLLR 667 | BFFFBFFLRR 668 | FBBBBBFLRR 669 | FBBBBBFRLR 670 | BFFBFFFRRR 671 | FFBFBBFLRL 672 | FFBFBBFRRL 673 | FFBFFBBRRR 674 | FFFFBBFRLL 675 | BBFBFBFLRL 676 | FBFBBFFLLL 677 | BBFFFFBLLL 678 | FBBBBFFRRR 679 | BFFBFBFRLL 680 | BFBBFBFRRL 681 | FFBFBBFLLL 682 | FBFFFBFRRR 683 | FFBFFBFLLR 684 | BFBBFFBRRL 685 | FBBFFBBRRR 686 | FFBFBBBRLL 687 | FBBBBBBLLR 688 | FBFBBFBRRL 689 | BFFBFFFRLL 690 | BBFFBBFRRR 691 | FBBBBBFRLL 692 | FBBFFFFRLR 693 | FBBFFBBRLR 694 | BFBFBFBLLR 695 | FBFFBFBRRL 696 | FFFBBFFRRL 697 | FFBFBBFRLR 698 | BFFBFBBRLR 699 | BFFFBFBRRR 700 | BFBBBFFRRL 701 | FBBBFBBLRR 702 | FFFBFBFRLL 703 | FFBBFFBRLL 704 | FFFBBBBRLR 705 | FBBBFBFLLR 706 | BFBFBBBRRL 707 | FBBBFFFRLR 708 | FBFFFFFLRR 709 | FFBFBFFRRR 710 | FFFBFBBLRR 711 | FBBFBFFLLL 712 | FFBFFBFRLL 713 | FFBBBFFLRR 714 | FFBFBFBRRR 715 | FFFBBBBLRL 716 | FFBFBFFLRL 717 | FBFBBFBLRL 718 | FFFBFBFLRL 719 | BFBFFBFLLR 720 | BBFFFBFRLL 721 | FFBBBBBLLR 722 | BBFFFBBLLL 723 | BBFBFFBRRR 724 | BFBFFFBRLL 725 | FFFBFBFRRL 726 | FFBFFFFLRR 727 | BBFFFFFLLL 728 | FBBBFFFLRR 729 | BFFFBBFRRR 730 | FBBFBBBLLR 731 | BFBFFFFRRL 732 | FFBBFFBRRL 733 | BFBFFBBLRL 734 | BBFFFBBRRR 735 | FFFBBFFRLL 736 | BFFBBFBRRL 737 | BFFFFFFLRR 738 | BFBBBBFRLR 739 | FBBFFBFRLR 740 | BFFBBFFRRL 741 | BFFBFFFRRL 742 | FFFFBBBRLL 743 | FBFBFFFRLR 744 | BBFFBBFRLR 745 | BBFFBBFRLL 746 | BFFFBBFLRL 747 | FBFFBFFLLR 748 | BFBBFBFLRL 749 | FBFBFBFRLL 750 | BFBFBBBRLR 751 | BFBFFBBLLR 752 | FBBBBFBRLR 753 | FBFFFFBLRR 754 | FBFFBBFRRR 755 | BBFFBFFLLL 756 | FFFBFBFLLL 757 | BFFBBFFRRR 758 | FBBFBFFRRR 759 | BBFFBBBRLR 760 | FFFBFFBLRR 761 | FBFBFBFRLR 762 | BFBBBBBRRR 763 | BFFBFBFLRL 764 | BBFFBBBLLL 765 | BBFFFFFLLR 766 | BFBFFBFRLL 767 | BFBBBBFRRL 768 | BFBBFFBLRL 769 | BFFBBFBLLL 770 | BFFBBBBLLL 771 | BFBFBBFLLL 772 | FBFBFFFRLL 773 | FBFBFBFRRL 774 | BFFBFBBLLL 775 | FFBBFBBRRR 776 | FFFBFFBRRR 777 | FBFFFBFLLR 778 | FBFFFFFRRR 779 | FBBFBBFLLL 780 | FFFBFBBRLR 781 | FBFFFFFLLL 782 | FBBFFFFRRL 783 | BFBBFFBLLR 784 | BBFBFBBLRL 785 | FBBFBBFLLR 786 | BFBFFFFLRL 787 | FFBBFBBLRR 788 | BFBBBFBLRL 789 | BFFBBFFRLR 790 | FFFFBFFRLL 791 | BFFBBFFLRR 792 | FBBBFBBRLR 793 | FBFBFFFLLL 794 | FFBBBFFRRL 795 | BFBBFBFRLR 796 | FBFFFBBRLL 797 | FFFBFFFLRL 798 | BFBBBFBRRL 799 | FBBFFFBRLR 800 | FFBBBBFLLL 801 | FBBBFFFLLR 802 | FBFFBBFLLR 803 | FFBFBBFRLL 804 | BBFFBFBRLR 805 | FBFFBBBLLR 806 | FFBFBFBRRL 807 | BFFBFFBLRL 808 | FBFFBFBLLR 809 | FFBFBFBLRR 810 | FFBBFBFLRL 811 | FFBBFFFRRL 812 | BFBFBFFLLR 813 | FBBBBBBRRR 814 | BFBFFFFRLR 815 | BFFBBBBRLL 816 | FFFFFBBRRR 817 | FFBFFFBRRR 818 | FFFBFFFLLL 819 | BFBFFFFRRR 820 | FBBBBBFRRR 821 | FBBBFFFRRL 822 | FBBFFFFRLL 823 | FFFFFBFLRR 824 | FFBFBBBLLL 825 | BFBFFBBRRL 826 | FFFFBBFLRL 827 | BFBBBFBRLL 828 | FFBFFFBLRL 829 | FBBFFFBRLL 830 | BFFBBFBLRR 831 | FFFBBFFLLR 832 | FBBFFBBRRL 833 | FFBFBBBRRR 834 | FBFBFFBLRL 835 | BFBBFBBRRR 836 | BBFFFFBLRL 837 | FBFBFBBLLR 838 | BFFFBBBLRR 839 | BFBFFFBRLR 840 | FFFBBBBLLL 841 | BFFBFFFLRR 842 | BFBBFFFRRL 843 | FBFFBBBLRL 844 | FBFBBFFLRR 845 | FBFFBBFRRL 846 | BBFBFFBRLL 847 | -------------------------------------------------------------------------------- /posts/2020/advent-of-code-day-8.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Advent of Code - Day 8: Handheld Halting\n" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## Part One\n", 15 | "\n", 16 | "Your flight to the major airline hub reaches cruising altitude without incident.\n", 17 | "While you consider checking the in-flight menu for one of those drinks that come\n", 18 | "with a little umbrella, you are interrupted by the kid sitting next to you.\n", 19 | "\n", 20 | "Their\n", 21 | "[handheld game console](https://en.wikipedia.org/wiki/Handheld_game_console)\n", 22 | "won't turn on! They ask if you can take a look.\n", 23 | "\n", 24 | "You narrow the problem down to a strange infinite loop in the boot code (your\n", 25 | "puzzle input) of the device. You should be able to fix it, but first you need to\n", 26 | "be able to run the code in isolation.\n", 27 | "\n", 28 | "The boot code is represented as a text file with one instruction per line of\n", 29 | "text. Each instruction consists of an operation (acc, jmp, or nop) and an\n", 30 | "argument (a signed number like +4 or -20).\n", 31 | "\n", 32 | "- `acc` increases or decreases a single global value called the accumulator by\n", 33 | " the value given in the argument. For example, acc +7 would increase the\n", 34 | " accumulator by 7. The accumulator starts at 0. After an acc instruction, the\n", 35 | " instruction immediately below it is executed next.\n", 36 | "- `jmp` jumps to a new instruction relative to itself. The next instruction to\n", 37 | " execute is found using the argument as an offset from the jmp instruction; for\n", 38 | " example, jmp +2 would skip the next instruction, jmp +1 would continue to the\n", 39 | " instruction immediately below it, and jmp -20 would cause the instruction 20\n", 40 | " lines above to be executed next.\n", 41 | "- `nop` stands for No OPeration - it does nothing. The instruction immediately\n", 42 | " below it is executed next.\n", 43 | "\n", 44 | "For example, consider the following program:\n", 45 | "\n", 46 | "```\n", 47 | "nop +0\n", 48 | "acc +1\n", 49 | "jmp +4\n", 50 | "acc +3\n", 51 | "jmp -3\n", 52 | "acc -99\n", 53 | "acc +1\n", 54 | "jmp -4\n", 55 | "acc +6\n", 56 | "```\n", 57 | "\n", 58 | "These instructions are visited in this order:\n", 59 | "\n", 60 | "```\n", 61 | "nop +0 | 1\n", 62 | "acc +1 | 2, 8(!)\n", 63 | "jmp +4 | 3\n", 64 | "acc +3 | 6\n", 65 | "jmp -3 | 7\n", 66 | "acc -99 |\n", 67 | "acc +1 | 4\n", 68 | "jmp -4 | 5\n", 69 | "acc +6 |\n", 70 | "```\n", 71 | "\n", 72 | "First, the nop +0 does nothing. Then, the accumulator is increased from 0 to 1\n", 73 | "(acc +1) and jmp +4 sets the next instruction to the other acc +1 near the\n", 74 | "bottom. After it increases the accumulator from 1 to 2, jmp -4 executes, setting\n", 75 | "the next instruction to the only acc +3. It sets the accumulator to 5, and jmp\n", 76 | "-3 causes the program to continue back at the first acc +1.\n", 77 | "\n", 78 | "This is an infinite loop: with this sequence of jumps, the program will run\n", 79 | "forever. The moment the program tries to run any instruction a second time, you\n", 80 | "know it will never terminate.\n", 81 | "\n", 82 | "Immediately before the program would run an instruction a second time, the value\n", 83 | "in the accumulator is 5.\n", 84 | "\n", 85 | "Run your copy of the boot code. Immediately before any instruction is executed a\n", 86 | "second time, **what value is in the accumulator?**\n" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "## Load and Clean Input data\n" 94 | ] 95 | }, 96 | { 97 | "cell_type": "markdown", 98 | "metadata": {}, 99 | "source": [ 100 | ":::{note} The input data can be found\n", 101 | "[here](https://adventofcode.com/2020/day/8). :::\n" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": 1, 107 | "metadata": {}, 108 | "outputs": [ 109 | { 110 | "name": "stdout", 111 | "output_type": "stream", 112 | "text": [ 113 | "656\n", 114 | "[Instruction(operation='acc', argument=-7), Instruction(operation='acc', argument=2)]\n" 115 | ] 116 | } 117 | ], 118 | "source": [ 119 | "from collections import namedtuple\n", 120 | "\n", 121 | "\n", 122 | "def parse_instruction(instruction):\n", 123 | " a, b = instruction.strip().split(' ')\n", 124 | " return Instruction(a, int(b))\n", 125 | "\n", 126 | "\n", 127 | "with open('../../data/advent-of-code/2020/day-8-input') as fid:\n", 128 | " data = fid.readlines()\n", 129 | " Instruction = namedtuple('Instruction', ['operation', 'argument'])\n", 130 | " data = [parse_instruction(x) for x in data]\n", 131 | "\n", 132 | "print(len(data))\n", 133 | "print(data[0:2])" 134 | ] 135 | }, 136 | { 137 | "cell_type": "code", 138 | "execution_count": 2, 139 | "metadata": {}, 140 | "outputs": [ 141 | { 142 | "data": { 143 | "text/plain": [ 144 | "[Instruction(operation='nop', argument=0),\n", 145 | " Instruction(operation='acc', argument=1),\n", 146 | " Instruction(operation='jmp', argument=4),\n", 147 | " Instruction(operation='acc', argument=3),\n", 148 | " Instruction(operation='jmp', argument=-3),\n", 149 | " Instruction(operation='acc', argument=-99),\n", 150 | " Instruction(operation='acc', argument=1),\n", 151 | " Instruction(operation='jmp', argument=-4),\n", 152 | " Instruction(operation='acc', argument=6)]" 153 | ] 154 | }, 155 | "execution_count": 2, 156 | "metadata": {}, 157 | "output_type": "execute_result" 158 | } 159 | ], 160 | "source": [ 161 | "test_data = \"\"\"\n", 162 | "nop +0\n", 163 | "acc +1\n", 164 | "jmp +4\n", 165 | "acc +3\n", 166 | "jmp -3\n", 167 | "acc -99\n", 168 | "acc +1\n", 169 | "jmp -4\n", 170 | "acc +6\n", 171 | "\"\"\".strip().split('\\n')\n", 172 | "\n", 173 | "clean_test_data = [parse_instruction(instruction) for instruction in test_data]\n", 174 | "clean_test_data" 175 | ] 176 | }, 177 | { 178 | "cell_type": "markdown", 179 | "metadata": {}, 180 | "source": [ 181 | "### Solution\n" 182 | ] 183 | }, 184 | { 185 | "cell_type": "code", 186 | "execution_count": 3, 187 | "metadata": {}, 188 | "outputs": [], 189 | "source": [ 190 | "def find_program_status(instructions):\n", 191 | " accumulator, pointer = 0, 0\n", 192 | " visited = set()\n", 193 | " terminated = False\n", 194 | "\n", 195 | " while not terminated and pointer not in visited:\n", 196 | " instruction = instructions[pointer]\n", 197 | " visited.add(pointer)\n", 198 | "\n", 199 | " if instruction.operation == 'nop':\n", 200 | " pointer += 1\n", 201 | " elif instruction.operation == 'jmp':\n", 202 | " pointer += instruction.argument\n", 203 | "\n", 204 | " elif instruction.operation == 'acc':\n", 205 | " pointer += 1\n", 206 | " accumulator += instruction.argument\n", 207 | "\n", 208 | " terminated = pointer == len(instructions)\n", 209 | "\n", 210 | " return accumulator, terminated" 211 | ] 212 | }, 213 | { 214 | "cell_type": "code", 215 | "execution_count": 4, 216 | "metadata": {}, 217 | "outputs": [ 218 | { 219 | "data": { 220 | "text/plain": [ 221 | "(5, False)" 222 | ] 223 | }, 224 | "execution_count": 4, 225 | "metadata": {}, 226 | "output_type": "execute_result" 227 | } 228 | ], 229 | "source": [ 230 | "accumulator, terminated = find_program_status(clean_test_data)\n", 231 | "accumulator, terminated" 232 | ] 233 | }, 234 | { 235 | "cell_type": "code", 236 | "execution_count": 5, 237 | "metadata": {}, 238 | "outputs": [ 239 | { 240 | "data": { 241 | "text/plain": [ 242 | "(1594, False)" 243 | ] 244 | }, 245 | "execution_count": 5, 246 | "metadata": {}, 247 | "output_type": "execute_result" 248 | } 249 | ], 250 | "source": [ 251 | "accumulator, terminated = find_program_status(data)\n", 252 | "accumulator, terminated" 253 | ] 254 | }, 255 | { 256 | "cell_type": "markdown", 257 | "metadata": {}, 258 | "source": [ 259 | "## Part Two\n", 260 | "\n", 261 | "After some careful analysis, you believe **that exactly one instruction is\n", 262 | "corrupted**.\n", 263 | "\n", 264 | "Somewhere in the program, either a `jmp` is supposed to be a `nop`, or a `nop`\n", 265 | "is supposed to be a `jmp`. (No `acc` instructions were harmed in the corruption\n", 266 | "of this boot code.)\n", 267 | "\n", 268 | "The program is supposed to terminate by attempting to execute an instruction\n", 269 | "immediately after the last instruction in the file. By changing exactly one\n", 270 | "`jmp` or `nop`, you can repair the boot code and make it terminate correctly.\n", 271 | "\n", 272 | "For example, consider the same program from above:\n", 273 | "\n", 274 | "```\n", 275 | "nop +0\n", 276 | "acc +1\n", 277 | "jmp +4\n", 278 | "acc +3\n", 279 | "jmp -3\n", 280 | "acc -99\n", 281 | "acc +1\n", 282 | "jmp -4\n", 283 | "acc +6\n", 284 | "```\n", 285 | "\n", 286 | "If you change the first instruction from `nop +0` to `jmp +0`, it would create a\n", 287 | "single-instruction infinite loop, never leaving that instruction. If you change\n", 288 | "almost any of the jmp instructions, the program will still eventually find\n", 289 | "another jmp instruction and loop forever.\n", 290 | "\n", 291 | "However, if you change the second-to-last instruction (from `jmp -4` to\n", 292 | "`nop -4`), the program terminates! The instructions are visited in this order:\n", 293 | "\n", 294 | "```\n", 295 | "nop +0 | 1\n", 296 | "acc +1 | 2\n", 297 | "jmp +4 | 3\n", 298 | "acc +3 |\n", 299 | "jmp -3 |\n", 300 | "acc -99 |\n", 301 | "acc +1 | 4\n", 302 | "nop -4 | 5\n", 303 | "acc +6 | 6\n", 304 | "```\n", 305 | "\n", 306 | "After the last instruction (`acc +6`), the program terminates by attempting to\n", 307 | "run the instruction below the last instruction in the file. With this change,\n", 308 | "after the program terminates, the accumulator contains the value 8 (`acc +1`,\n", 309 | "`acc +1`, `acc +6`).\n", 310 | "\n", 311 | "Fix the program so that it terminates normally by changing exactly one `jmp` (to\n", 312 | "`nop`) or `nop` (to `jmp`). **What is the value of the accumulator after the\n", 313 | "program terminates?**\n" 314 | ] 315 | }, 316 | { 317 | "cell_type": "markdown", 318 | "metadata": {}, 319 | "source": [ 320 | "### Solution\n" 321 | ] 322 | }, 323 | { 324 | "cell_type": "code", 325 | "execution_count": 6, 326 | "metadata": {}, 327 | "outputs": [ 328 | { 329 | "data": { 330 | "text/plain": [ 331 | "8" 332 | ] 333 | }, 334 | "execution_count": 6, 335 | "metadata": {}, 336 | "output_type": "execute_result" 337 | } 338 | ], 339 | "source": [ 340 | "import copy\n", 341 | "\n", 342 | "\n", 343 | "def fix_program(data):\n", 344 | " instructions = copy.deepcopy(data)\n", 345 | " flip = (\n", 346 | " lambda x: x._replace(operation='jmp')\n", 347 | " if x.operation == 'nop'\n", 348 | " else x._replace(operation='nop')\n", 349 | " )\n", 350 | " for idx, instruction in enumerate(instructions):\n", 351 | " if instruction.operation == 'nop' or instruction.operation == 'jmp':\n", 352 | " previous = instruction\n", 353 | " instructions[idx] = flip(instruction)\n", 354 | " accumulator, terminated = find_program_status(instructions)\n", 355 | " if terminated:\n", 356 | " break\n", 357 | " instructions[idx] = previous\n", 358 | " return accumulator\n", 359 | "\n", 360 | "\n", 361 | "fix_program(clean_test_data)" 362 | ] 363 | }, 364 | { 365 | "cell_type": "code", 366 | "execution_count": 7, 367 | "metadata": {}, 368 | "outputs": [ 369 | { 370 | "name": "stdout", 371 | "output_type": "stream", 372 | "text": [ 373 | "The value of the accumulator after the program terminates is: 758\n" 374 | ] 375 | } 376 | ], 377 | "source": [ 378 | "print(f'The value of the accumulator after the program terminates is: {fix_program(data)}')" 379 | ] 380 | } 381 | ], 382 | "metadata": { 383 | "author": "Anderson Banihirwe", 384 | "date": "2020-12-08", 385 | "kernelspec": { 386 | "display_name": "Python 3", 387 | "language": "python", 388 | "name": "python3" 389 | }, 390 | "language_info": { 391 | "codemirror_mode": { 392 | "name": "ipython", 393 | "version": 3 394 | }, 395 | "file_extension": ".py", 396 | "mimetype": "text/x-python", 397 | "name": "python", 398 | "nbconvert_exporter": "python", 399 | "pygments_lexer": "ipython3", 400 | "version": "3.8.8" 401 | }, 402 | "tags": "python,adventofcode", 403 | "title": "Advent of Code - Day 8: Handheld Halting", 404 | "widgets": { 405 | "application/vnd.jupyter.widget-state+json": { 406 | "state": {}, 407 | "version_major": 2, 408 | "version_minor": 0 409 | } 410 | } 411 | }, 412 | "nbformat": 4, 413 | "nbformat_minor": 4 414 | } 415 | -------------------------------------------------------------------------------- /data/advent-of-code/2020/day-9-input: -------------------------------------------------------------------------------- 1 | 14 2 | 9 3 | 43 4 | 18 5 | 13 6 | 24 7 | 3 8 | 38 9 | 33 10 | 8 11 | 41 12 | 4 13 | 32 14 | 15 15 | 31 16 | 44 17 | 17 18 | 34 19 | 21 20 | 10 21 | 50 22 | 37 23 | 2 24 | 23 25 | 6 26 | 47 27 | 5 28 | 9 29 | 11 30 | 16 31 | 12 32 | 18 33 | 7 34 | 13 35 | 26 36 | 14 37 | 19 38 | 8 39 | 20 40 | 15 41 | 29 42 | 58 43 | 17 44 | 22 45 | 21 46 | 24 47 | 25 48 | 10 49 | 27 50 | 31 51 | 33 52 | 23 53 | 28 54 | 30 55 | 34 56 | 32 57 | 35 58 | 38 59 | 49 60 | 18 61 | 26 62 | 36 63 | 37 64 | 47 65 | 39 66 | 56 67 | 40 68 | 42 69 | 43 70 | 41 71 | 44 72 | 45 73 | 46 74 | 48 75 | 50 76 | 51 77 | 53 78 | 79 79 | 73 80 | 69 81 | 117 82 | 54 83 | 55 84 | 90 85 | 57 86 | 62 87 | 75 88 | 114 89 | 85 90 | 94 91 | 110 92 | 81 93 | 98 94 | 153 95 | 86 96 | 89 97 | 204 98 | 96 99 | 101 100 | 103 101 | 104 102 | 152 103 | 169 104 | 124 105 | 135 106 | 109 107 | 143 108 | 171 109 | 119 110 | 132 111 | 137 112 | 207 113 | 166 114 | 167 115 | 184 116 | 185 117 | 170 118 | 175 119 | 182 120 | 187 121 | 190 122 | 197 123 | 233 124 | 204 125 | 212 126 | 244 127 | 299 128 | 228 129 | 339 130 | 241 131 | 246 132 | 269 133 | 415 134 | 319 135 | 298 136 | 349 137 | 337 138 | 333 139 | 444 140 | 357 141 | 345 142 | 362 143 | 365 144 | 562 145 | 377 146 | 387 147 | 401 148 | 416 149 | 541 150 | 469 151 | 472 152 | 474 153 | 647 154 | 591 155 | 487 156 | 515 157 | 567 158 | 617 159 | 686 160 | 1158 161 | 793 162 | 670 163 | 678 164 | 702 165 | 864 166 | 710 167 | 856 168 | 781 169 | 817 170 | 764 171 | 1251 172 | 1214 173 | 885 174 | 941 175 | 943 176 | 946 177 | 961 178 | 1002 179 | 1082 180 | 1456 181 | 1269 182 | 1184 183 | 2104 184 | 1364 185 | 1348 186 | 2032 187 | 1372 188 | 1742 189 | 1412 190 | 1474 191 | 1792 192 | 1545 193 | 1649 194 | 2305 195 | 2358 196 | 1826 197 | 2725 198 | 1828 199 | 3282 200 | 2043 201 | 1907 202 | 1963 203 | 2910 204 | 2266 205 | 2814 206 | 2453 207 | 2532 208 | 2712 209 | 2957 210 | 2720 211 | 2784 212 | 3903 213 | 3019 214 | 3123 215 | 4131 216 | 4006 217 | 3789 218 | 3871 219 | 3654 220 | 4811 221 | 3735 222 | 3791 223 | 5866 224 | 4309 225 | 4416 226 | 4439 227 | 4229 228 | 4719 229 | 5576 230 | 5237 231 | 8578 232 | 5244 233 | 6447 234 | 5504 235 | 8510 236 | 7445 237 | 9570 238 | 7920 239 | 6777 240 | 8725 241 | 7389 242 | 12460 243 | 7606 244 | 13305 245 | 15899 246 | 9992 247 | 14957 248 | 8748 249 | 12843 250 | 8855 251 | 14092 252 | 8948 253 | 9956 254 | 10741 255 | 10481 256 | 10748 257 | 11951 258 | 25705 259 | 12281 260 | 26647 261 | 14697 262 | 14166 263 | 25855 264 | 19473 265 | 16244 266 | 24048 267 | 16354 268 | 16461 269 | 36446 270 | 22914 271 | 17603 272 | 20704 273 | 28195 274 | 17803 275 | 18811 276 | 29429 277 | 18904 278 | 20437 279 | 41380 280 | 24914 281 | 22699 282 | 46409 283 | 26447 284 | 26978 285 | 57624 286 | 33639 287 | 33847 288 | 32598 289 | 41141 290 | 39160 291 | 33957 292 | 70570 293 | 34064 294 | 35406 295 | 36414 296 | 80323 297 | 50894 298 | 36614 299 | 36707 300 | 48333 301 | 63592 302 | 39341 303 | 43136 304 | 66445 305 | 59313 306 | 49146 307 | 92952 308 | 99043 309 | 99227 310 | 73028 311 | 66237 312 | 69012 313 | 66555 314 | 70771 315 | 68021 316 | 107362 317 | 69470 318 | 70478 319 | 71820 320 | 73121 321 | 91469 322 | 76048 323 | 73321 324 | 107478 325 | 108353 326 | 122905 327 | 142591 328 | 92282 329 | 108459 330 | 115383 331 | 125194 332 | 142791 333 | 137491 334 | 135567 335 | 132792 336 | 199928 337 | 134576 338 | 163289 339 | 138499 340 | 334504 341 | 205037 342 | 197014 343 | 142298 344 | 144941 345 | 267785 346 | 149369 347 | 271642 348 | 165603 349 | 237223 350 | 200635 351 | 207665 352 | 227849 353 | 200741 354 | 352606 355 | 259770 356 | 257986 357 | 431074 358 | 310544 359 | 267368 360 | 283440 361 | 291667 362 | 273075 363 | 304102 364 | 402068 365 | 287239 366 | 294310 367 | 307901 368 | 314972 369 | 345682 370 | 350004 371 | 350110 372 | 437245 373 | 612376 374 | 401376 375 | 408300 376 | 435514 377 | 636046 378 | 695539 379 | 675143 380 | 547009 381 | 525354 382 | 540443 383 | 684816 384 | 550808 385 | 1076162 386 | 560314 387 | 567385 388 | 933654 389 | 581549 390 | 595140 391 | 602211 392 | 664976 393 | 968614 394 | 695686 395 | 700114 396 | 751486 397 | 809676 398 | 1044346 399 | 926730 400 | 1318871 401 | 960868 402 | 1065797 403 | 1091251 404 | 1225951 405 | 1646557 406 | 2157048 407 | 1121992 408 | 1153019 409 | 1519422 410 | 1127699 411 | 1155454 412 | 1277235 413 | 1176689 414 | 1183760 415 | 1197351 416 | 1267187 417 | 1360662 418 | 1626844 419 | 1395800 420 | 1712354 421 | 2213243 422 | 1736406 423 | 1887598 424 | 1992527 425 | 2088567 426 | 2026665 427 | 2187789 428 | 2218950 429 | 2277446 430 | 2329708 431 | 2249691 432 | 2280718 433 | 2283153 434 | 2304388 435 | 2311459 436 | 3516878 437 | 2453924 438 | 2544422 439 | 5605445 440 | 2464538 441 | 3353189 442 | 3949649 443 | 3132206 444 | 3108154 445 | 5319995 446 | 5686055 447 | 3914263 448 | 3880125 449 | 5220773 450 | 4115232 451 | 4214454 452 | 4437480 453 | 4468641 454 | 4554079 455 | 4563871 456 | 4530409 457 | 5807113 458 | 4587541 459 | 4615847 460 | 4998346 461 | 6378801 462 | 10826218 463 | 5008960 464 | 5596744 465 | 7018617 466 | 6485395 467 | 6240360 468 | 6988279 469 | 7022417 470 | 9022720 471 | 8530110 472 | 7794388 473 | 7995357 474 | 8329686 475 | 8552712 476 | 8906121 477 | 13145957 478 | 8999050 479 | 12016963 480 | 12615361 481 | 9528755 482 | 15015505 483 | 14537715 484 | 10007306 485 | 13258977 486 | 10605704 487 | 12027577 488 | 13504012 489 | 11837104 490 | 15518389 491 | 13228639 492 | 14782667 493 | 27928624 494 | 14816805 495 | 15789745 496 | 16525467 497 | 16325043 498 | 16548069 499 | 21016013 500 | 21844410 501 | 17905171 502 | 23751661 503 | 20134459 504 | 26930747 505 | 19536061 506 | 20613010 507 | 22034883 508 | 22442808 509 | 31843432 510 | 25096081 511 | 29553682 512 | 36661215 513 | 25065743 514 | 26619771 515 | 29018384 516 | 36817550 517 | 38968275 518 | 30606550 519 | 41591210 520 | 32114788 521 | 25918798 522 | 36084130 523 | 34453240 524 | 37441232 525 | 38039630 526 | 47100626 527 | 63748297 528 | 57537297 529 | 40149071 530 | 43055818 531 | 42647893 532 | 44477691 533 | 47508551 534 | 54114465 535 | 61883293 536 | 50984541 537 | 56525348 538 | 51685514 539 | 52538569 540 | 54937182 541 | 58033586 542 | 69574825 543 | 93966580 544 | 60372038 545 | 103019931 546 | 62002928 547 | 70537370 548 | 95576927 549 | 75480862 550 | 117862762 551 | 82796964 552 | 178500793 553 | 112987469 554 | 123689290 555 | 85703711 556 | 107880589 557 | 174870762 558 | 103523110 559 | 124512007 560 | 102670055 561 | 120036514 562 | 125474552 563 | 161053517 564 | 107475751 565 | 112970768 566 | 169478679 567 | 122374966 568 | 130909408 569 | 211178263 570 | 132540298 571 | 137483790 572 | 183361451 573 | 278938378 574 | 198674479 575 | 215640823 576 | 293167969 577 | 416857259 578 | 218244009 579 | 188373766 580 | 193179462 581 | 249986559 582 | 206193165 583 | 220446519 584 | 227182062 585 | 210145806 586 | 294953231 587 | 229850717 588 | 235345734 589 | 244959541 590 | 243880176 591 | 382035930 592 | 319283174 593 | 367886032 594 | 270024088 595 | 315901749 596 | 320845241 597 | 371735217 598 | 468230568 599 | 592181736 600 | 381553228 601 | 544939790 602 | 418224483 603 | 394566931 604 | 436043882 605 | 399372627 606 | 416338971 607 | 426639684 608 | 439996523 609 | 474810258 610 | 611886647 611 | 465196451 612 | 473730893 613 | 480305275 614 | 743754981 615 | 710468680 616 | 1175665131 617 | 585925837 618 | 590869329 619 | 636746990 620 | 796207024 621 | 760841764 622 | 891955376 623 | 776120159 624 | 780925855 625 | 793939558 626 | 810905902 627 | 826012311 628 | 1026913211 629 | 835416509 630 | 815711598 631 | 842978655 632 | 906944959 633 | 905192974 634 | 938927344 635 | 1077083098 636 | 945501726 637 | 954036168 638 | 1066231112 639 | 1845872303 640 | 1176795166 641 | 1970734724 642 | 1222672827 643 | 1387076353 644 | 1761213324 645 | 1536961923 646 | 1726427581 647 | 2016010442 648 | 1574865413 649 | 1591831757 650 | 1739441284 651 | 1720904572 652 | 1754638942 653 | 2768626923 654 | 3420737716 655 | 1844120318 656 | 1748171629 657 | 1812137933 658 | 1850694700 659 | 1884429070 660 | 1899537894 661 | 2011732838 662 | 2020267280 663 | 3595132693 664 | 3860130760 665 | 3199214286 666 | 2609749180 667 | 3862427538 668 | 3141715295 669 | 3111827336 670 | 5215224728 671 | 3425560113 672 | 3166697170 673 | 3295769985 674 | 3312736329 675 | 3460345856 676 | 3469076201 677 | 3502810571 678 | 3560309562 679 | 3592291947 680 | 3598866329 681 | 7027362661 682 | 5049908986 683 | 4494178250 684 | 3896161908 685 | 4509287074 686 | 4032000118 687 | 5523077851 688 | 7020655418 689 | 6963156427 690 | 5721576516 691 | 5751464475 692 | 6424563665 693 | 6253542631 694 | 6278524506 695 | 8718035299 696 | 6635773371 697 | 9753576634 698 | 10572986837 699 | 6905028276 700 | 6929422057 701 | 7488453855 702 | 7095102518 703 | 7152601509 704 | 7191158276 705 | 14803485620 706 | 14583556373 707 | 7928162026 708 | 10859318335 709 | 13788374880 710 | 8541287192 711 | 9555077969 712 | 11244654367 713 | 12005007106 714 | 14417875912 715 | 11473040991 716 | 12029988981 717 | 13060337036 718 | 12532067137 719 | 12914297877 720 | 13540801647 721 | 13730875889 722 | 13834450333 723 | 14000130794 724 | 14024524575 725 | 14082023566 726 | 20883477398 727 | 26072868784 728 | 14343759785 729 | 15119320302 730 | 19933169132 731 | 22959163104 732 | 16469449218 733 | 29488247101 734 | 20799732336 735 | 18096365161 736 | 21028118960 737 | 23274643348 738 | 55561115885 739 | 24533378027 740 | 26894787369 741 | 24562056118 742 | 27540932441 743 | 25446365014 744 | 69561246679 745 | 27271677536 746 | 27565326222 747 | 27834581127 748 | 48424409839 749 | 28106548141 750 | 28425783351 751 | 42629743188 752 | 44991152530 753 | 35143492121 754 | 50169430717 755 | 40732901468 756 | 34565814379 757 | 37497568178 758 | 38896097497 759 | 39124484121 760 | 51456843487 761 | 44302762308 762 | 51700426699 763 | 49095434145 764 | 77710363158 765 | 50008421132 766 | 51833733654 767 | 54837003758 768 | 56532331492 769 | 55106258663 770 | 55378225677 771 | 55399907349 772 | 63569275472 773 | 62672362520 774 | 80134644651 775 | 93398196453 776 | 73690298500 777 | 76622052299 778 | 69709306500 779 | 72063382557 780 | 73461911876 781 | 106856750836 782 | 76393665675 783 | 115746536420 784 | 90958217775 785 | 119204694012 786 | 105627765637 787 | 99103855277 788 | 148235200211 789 | 135512870328 790 | 104845424890 791 | 110778133026 792 | 109943262421 793 | 195803642665 794 | 110484484340 795 | 118969182821 796 | 216800013257 797 | 209588339617 798 | 132381669020 799 | 141772689057 800 | 195591235120 801 | 267867025222 802 | 187809918977 803 | 303556455397 804 | 145525294433 805 | 149855577551 806 | 167351883450 807 | 181239090565 808 | 201442702115 809 | 190062073052 810 | 224832459649 811 | 204731620914 812 | 260741871878 813 | 215623557916 814 | 214788687311 815 | 306288127005 816 | 274154358077 817 | 355161802427 818 | 400322856034 819 | 252257173397 820 | 313620759585 821 | 277906963453 822 | 282237246571 823 | 356561376368 824 | 506416953919 825 | 295380871984 826 | 442319246449 827 | 312877177883 828 | 317207461001 829 | 326764384998 830 | 604671348451 831 | 348590974015 832 | 385970711479 833 | 391504775167 834 | 569950489738 835 | 419520308225 836 | 883571249323 837 | 430412245227 838 | 467045860708 839 | 488943045388 840 | 526411531474 841 | 708712236168 842 | 530164136850 843 | 640385144583 844 | 577618118555 845 | 591527723038 846 | 612588332985 847 | 595114424454 848 | 769083631447 849 | 747619706228 850 | 608258049867 851 | 908735184039 852 | 630084638884 853 | 643971845999 854 | 712735096477 855 | 897458105935 856 | 734561685494 857 | 777475486646 858 | 1027778358092 859 | 1197655281556 860 | 849932553452 861 | 919355290615 862 | 1021939968265 863 | 955988906096 864 | 1019107182238 865 | 1260973216968 866 | 1107782255405 867 | 1284356990582 868 | 1169145841593 869 | 1480017192336 870 | 1221612361922 871 | 1203372474321 872 | 1225199063338 873 | 1238342688751 874 | 1356706942476 875 | 1252229895866 876 | 1274056484883 877 | 1562667649929 878 | 2066603947528 879 | 2216762463794 880 | 1753668867732 881 | 1627408040098 882 | 1696830777261 883 | 1769287844067 884 | 2922814709325 885 | 2144554353953 886 | 1938462472853 887 | 3878803615421 888 | 2063771161501 889 | 2126889437643 890 | 2276928096998 891 | 2311154729726 892 | 2372518315914 893 | 4619645486586 894 | 2428571537659 895 | 2424984836243 896 | 2441715163072 897 | 4310980788767 898 | 3379119333509 899 | 2919374592405 900 | 2526286380749 901 | 2836724134812 902 | 4046215941065 903 | 6798178207826 904 | 3324238817359 905 | 3381076907830 906 | 6374751950268 907 | 3466118621328 908 | 3707750316920 909 | 6630565026245 910 | 4065351910496 911 | 8189784404188 912 | 4190660599144 913 | 4340699258499 914 | 8803323487927 915 | 4588082826724 916 | 4683673045640 917 | 4797503152157 918 | 7048217024245 919 | 9967591616650 920 | 5361089755477 921 | 4968001543821 922 | 5907363288579 923 | 6984726502901 924 | 5363010515561 925 | 15174510907089 926 | 8406051168995 927 | 6705315725189 928 | 6790357438687 929 | 8178580059987 930 | 8391423362560 931 | 7656779220472 932 | 8988163751301 933 | 10821225625389 934 | 9024372304139 935 | 8531359857643 936 | 8778743425868 937 | 8874333644784 938 | 8928782085223 939 | 9271755872364 940 | 11782229655058 941 | 9765504695978 942 | 10158592907634 943 | 12347737018462 944 | 10331012059382 945 | 16184236140950 946 | 14237344160345 947 | 11270373804140 948 | 14351174266862 949 | 14447136659159 950 | 13495673163876 951 | 15096739087749 952 | 14883895785176 953 | 17415795666699 954 | 28531973159412 955 | 17519523608944 956 | 16585561305695 957 | 17405693502427 958 | 17803115730007 959 | 17555732161782 960 | 32439627946958 961 | 17653077070652 962 | 20144707448924 963 | 20542129676504 964 | 32652471249531 965 | 24649400481154 966 | 19924097603612 967 | 20489604967016 968 | 21601385863522 969 | 23826685223258 970 | 24766046968016 971 | 48476085704412 972 | 25621548071002 973 | 27846847430738 974 | 31966660268103 975 | 28379568949052 976 | 29980634872925 977 | 34001356972394 978 | 33991254808122 979 | 44910754416940 980 | 61838102238860 981 | 37127690982199 982 | 34961425664209 983 | 41525483467134 984 | 35208809232434 985 | 48336452397754 986 | 37577174674264 987 | 40068805052536 988 | 53568046131625 989 | 40413702570628 990 | 47222933934524 991 | 68448374001588 992 | 42090990830538 993 | 45428071086780 994 | 54746681840941 995 | 50387595039018 996 | 53468395501740 997 | 54001117020054 998 | 56226416379790 999 | 58360203821977 1000 | 62380925921446 1001 | --------------------------------------------------------------------------------